<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
   
  <meta name="keywords" content="Java Python" />
   
  <meta name="description" content="From Zero to Hero" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    23种设计模式-详细总结 |  朱酱酱的学习博客
  </title>
  <meta name="generator" content="hexo-theme-yilia-plus">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/css/style.css">

  
<script src="/js/pace.min.js"></script>


  

  

<link rel="alternate" href="/atom.xml" title="朱酱酱的学习博客" type="application/atom+xml">
</head>

</html>

<body>
  <div id="app">
    <main class="content">
      <section class="outer">
  <article id="post-GoF23/23种设计模式-详细总结" class="article article-type-post" itemscope
  itemprop="blogPost" data-scroll-reveal>

  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  23种设计模式-详细总结
</h1>
  

    </header>
    

    
    <div class="article-meta">
      <a href="/2020/06/09/GoF23/23%E7%A7%8D%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%AF%A6%E7%BB%86%E6%80%BB%E7%BB%93/" class="article-date">
  <time datetime="2020-06-09T11:02:24.000Z" itemprop="datePublished">2020-06-09</time>
</a>
      
      
      
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">25.1k字</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">101分钟</span>
        </span>
    </span>
</div>

      
    </div>
    

    
    
    <div class="tocbot"></div>





    

    <div class="article-entry" itemprop="articleBody">
      


      

      
      <h1 id="23种设计模式-详细总结"><a href="#23种设计模式-详细总结" class="headerlink" title="23种设计模式-详细总结"></a>23种设计模式-详细总结</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200610/221023280.png" alt="mark"></p>
<ul>
<li>设计模式是解决问题的方案，学习现有的设计模式可以做到经验的复用</li>
<li>拥有设计模式的词汇，在沟通是就能用到更少的词汇来讨论，并且不需要知道底层的细节</li>
</ul>
<p><strong>话在前头：</strong></p>
<ol>
<li>什么是好的设计模式？</li>
</ol>
<ul>
<li>提高复用</li>
<li>应对变化</li>
</ul>
<ol start="2">
<li>在什么时候，什么地方使用设计模式？</li>
</ol>
<ul>
<li><p><strong>在需求频繁变化的变化点使用设计模式</strong></p>
</li>
<li><p><strong>Refactoring to Patterns(重构的方式一步一步到模式)</strong></p>
</li>
</ul>
<p>重构的关键方法：</p>
<ol>
<li>静态 -&gt; 动态</li>
<li>早绑定 -&gt; 晚绑定</li>
<li>继承 -&gt; 组合</li>
<li>编译时依赖 -&gt; 运行时依赖</li>
<li>紧耦合 -&gt; 松耦合</li>
</ol>
<a id="more"></a>

<h3 id="1-设计原则"><a href="#1-设计原则" class="headerlink" title="1. 设计原则"></a>1. 设计原则</h3><h4 id="1-1-依赖倒置原则-DIP"><a href="#1-1-依赖倒置原则-DIP" class="headerlink" title="1.1 依赖倒置原则(DIP)"></a>1.1 依赖倒置原则(DIP)</h4><ul>
<li><p>Dependence Inversion Principle，缩写DIP</p>
</li>
<li><p>高层次的模块不依赖于低层次模块的实现细节</p>
<ul>
<li>高层模块不应该依赖低层模块，两者都应该依赖其抽象</li>
<li>细节应该依赖抽象</li>
<li>抽象不应该依赖细节</li>
</ul>
</li>
</ul>
<p>是不是觉得和没说一个样，至少我是这么觉得的；继续往后看才明白，</p>
<ul>
<li>所谓高层模块就是调用端</li>
<li>低层模块就是具体的实现类</li>
<li><strong>依赖倒置原则在java中的表现就是：模块间的依赖通过抽象发生，实现类之间不发生直接的依赖关系</strong><ul>
<li><strong>也就是通过接口或者抽象类产生依赖关系（调用关系）</strong></li>
<li><strong>也就是面向接口编程或者说面向抽象编程</strong></li>
</ul>
</li>
</ul>
<p>其实依赖倒置原则主要目的就是解耦</p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200614/211520692.png" alt="mark"></p>
<p>可以使用这张图来表示，表达出来就是<code>ImageLoader</code>和<code>MemonyCache</code>等并没有直接关系，甚至<code>ImageLoader</code>只需要实现<code>ImageCache</code>类或继承其他已有的<code>ImageCache</code>子类完成相应的缓存功能，然后将具体的实现注入到<code>ImageLoader</code>即可实现缓存功能的替换。这也是依赖倒置原则的体现。</p>
<h4 id="1-2-开闭原则（OCP）"><a href="#1-2-开闭原则（OCP）" class="headerlink" title="1.2 开闭原则（OCP）"></a>1.2 开闭原则（OCP）</h4><ul>
<li><p>Open Close Principle，缩写OCP</p>
</li>
<li><p>定义：软件中得对象应该对于扩展是开放的，但是对于修改是封闭的。</p>
</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200614/211520692.png" alt="mark"></p>
<p><strong>简单地说，当软件需要变化时，应该尽量通过扩展的方式来实现变化，而不是通过修改已有的代码来实现。</strong></p>
<p>“应该尽量”4个字说明OCP原则并不是说绝对不可以修改原始类的，当代码需要需要重构的时候要及时重构，使代码恢复正常，而不是通过继承等方式添加新的实现，这会导致类型的膨胀以及历史遗留代码的冗余。</p>
<h4 id="1-3-单一职责原则（SRP）"><a href="#1-3-单一职责原则（SRP）" class="headerlink" title="1.3 单一职责原则（SRP）"></a>1.3 单一职责原则（SRP）</h4><ul>
<li>单一职责原则，就一个类而言，应该只有一个引起它变化的原因。</li>
<li>简单说，<strong>一个类</strong>应该是一组<strong>高度相关的函数</strong>、数据的封装；也就是高内聚。</li>
<li>Single Responsibility Principle，缩写SRP</li>
</ul>
<p>举个例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ImageLoader</span></span>&#123;</span><br><span class="line">    <span class="comment">//图片缓存</span></span><br><span class="line">    LruCache&lt;String,Bitmap&gt; mImageCache;</span><br><span class="line">    <span class="comment">//线程池，线程数量为CPU的数量</span></span><br><span class="line">    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProessors());</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ImageLoader</span><span class="params">()</span></span>&#123;</span><br><span class="line">        initImageCache();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initImageCache</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//省略...         </span></span><br><span class="line">   &#125;</span><br><span class="line">    </span><br><span class="line">   <span class="comment">//显示图片</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span>  <span class="keyword">void</span> <span class="title">displayImage</span><span class="params">(<span class="keyword">final</span> String url, <span class="keyword">final</span> ImageView imageView)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//省略... </span></span><br><span class="line">   &#125;</span><br><span class="line">    </span><br><span class="line">   <span class="comment">//下载图片</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span>  Bitmap <span class="title">downloadImage</span><span class="params">(String imageUrl)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//省略... </span></span><br><span class="line">        <span class="keyword">return</span> bitmap;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里可以看出来 <code>ImageLoader</code> 类作用有初始化图片缓存、显示图片、下载图片，显然显示图片和下载图片两个方法与初始化图片缓存方法相比作用就显得有些不相关。(也就是不符合单一职责原则)。</p>
<p>按照逻辑进行拆分后得到<code>ImageLoader</code>和<code>ImageCache</code>两个类。</p>
<ul>
<li><code>ImageLoader</code>负责图片加载逻辑</li>
<li><code>ImageCache</code>负责处理图片缓存逻辑</li>
<li>这样职责就很清楚了，当与缓存相关的逻辑需要改变时，不需要修改<code>ImageLoader</code>类，而图片加载的逻辑需要修改时也不会影响到缓存处理逻辑。</li>
</ul>
<p>修改后代码如下所示：</p>
<ul>
<li>添加的<code>ImageCache</code>类用于处理图片缓存，具体代码如下：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ImageCache</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 图片LRU缓存</span></span><br><span class="line">    LruCache&lt;String, Bitmap&gt; mImageCache;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ImageCache</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        initImageCache();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initImageCache</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//省略... </span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(String url, Bitmap bitmap)</span> </span>&#123;</span><br><span class="line">        mImageCache.put(url, bitmap) ;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Bitmap <span class="title">get</span><span class="params">(String url)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mImageCache.get(url) ;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><code>ImageLoader</code>代码修改如下所示：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 图片加载类 */</span></span><br><span class="line"><span class="keyword">public</span>  <span class="class"><span class="keyword">class</span> <span class="title">ImageLoader</span> </span>&#123;</span><br><span class="line">    <span class="comment">//图片缓存</span></span><br><span class="line">    ImageCache mImageCache = <span class="keyword">new</span> ImageCache() ;</span><br><span class="line">    <span class="comment">//线程池,线程数量为CPU的数量</span></span><br><span class="line">    ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());</span><br><span class="line"></span><br><span class="line">    <span class="comment">//加载图片</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span>  <span class="keyword">void</span> <span class="title">displayImage</span><span class="params">(<span class="keyword">final</span> String url, <span class="keyword">final</span> ImageView imageView)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//省略... </span></span><br><span class="line">     &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span>  Bitmap <span class="title">downloadImage</span><span class="params">(String imageUrl)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//省略... </span></span><br><span class="line">        <span class="keyword">return</span> bitmap;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="1-4-里氏替换原则-LSP"><a href="#1-4-里氏替换原则-LSP" class="headerlink" title="1.4 里氏替换原则 (LSP)"></a>1.4 里氏替换原则 (LSP)</h4><ul>
<li>Liskov Substitution Principle</li>
<li>里氏替换原则，书上原话的定义简直看不得（解释的辣眼睛，完全看不懂），简单的来说就是所有引用基类的地方必须能透明的时候其子类的对现象。只要父类能出现的地方子类就能出现，而且替换成子类也不会产生任何的错误和异常。使用者可能根本就不需要知道是父类还是子类。</li>
<li>但是，反过来就不行了，有子类出现的地方，父类未必就能适应。其实就是：抽象。</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200615/145239391.png" alt="mark"></p>
<ul>
<li>上图可以看出，<code>Window</code>依赖于<code>View</code>，而<code>Button</code>和<code>TextView</code>继承<code>View</code>。这里任何继承自<code>View</code>类的子类都可以设置给<code>show()</code>方法，也就是<strong>里氏替换原则</strong>。</li>
<li>通过里氏替换，就可以自定义各式各样的View，然后传递给Window，并且将View显示到屏幕上。</li>
</ul>
<p><strong>里氏替换原则的核心原理是抽象，抽象又依赖于继承这个特性，继承的优缺点都相当明显</strong></p>
<p>优点：</p>
<ul>
<li>代码重用，减少创建类的成本，每个子类都拥有父类的方法和属性</li>
<li>子类与父类基本相似，但又与父类有所区别</li>
<li>提高代码的可扩展性</li>
</ul>
<p>缺点：</p>
<ul>
<li>继承是侵入性的，只要继承就必须拥有父类的所有属性和方法</li>
<li>可能造成子类代码冗余、灵活性降低，因为子类必须拥有父类的属性和方法</li>
</ul>
<p>事物总是具有两面性，如何权衡利与弊都是需要根据具体场景来做出选择并加以处理。</p>
<h4 id="1-5-接口隔离原则（ISP）"><a href="#1-5-接口隔离原则（ISP）" class="headerlink" title="1.5 接口隔离原则（ISP）"></a>1.5 接口隔离原则（ISP）</h4><ul>
<li><p>Interface Segregation Principle</p>
</li>
<li><p>接口隔离原则将非常庞大、臃肿的接口拆分成为更小的和更具体的接口；目的就是解耦。</p>
</li>
<li><p>这个原则的做法和单一职责有点相似，就是说接口中的方法保持更高的相关性，尽量少，避免调不需要的方法。</p>
</li>
<li><p><strong>java中类尽量不使用public</strong></p>
</li>
</ul>
<h4 id="1-6-迪米特法则（LOP）"><a href="#1-6-迪米特法则（LOP）" class="headerlink" title="1.6 迪米特法则（LOP）"></a>1.6 迪米特法则（LOP）</h4><ul>
<li><p>Law of Principle</p>
</li>
<li><p>迪米特法则还有一个英文解释是：Only talk to your immedate friends，翻译过来就是：只与直接的朋友通信。（什么叫做直接的朋友，每个对象都必然会与其他对象有耦合关系，两个对象之间的耦合就成为朋友关系，这种关系类型有很多，例如：组合，聚合，依赖等）</p>
</li>
<li><p>迪米特原则也称为最少知识原则（Least Knowledge Principle），定义：一个对象应该对其他对象有最少的了解。通俗地讲，一个类要对自己需要调用的类知道得最少，类的内部如何实现、如何复杂都与调用者（或依赖者）没关系，调用者（或依赖者）只需要知道他需要的方法即可，其他的不需要关心。</p>
</li>
<li><p>类与类之间的关系越密切，耦合度越大；当一个类发生改变时，对另一个类的影响也越大。</p>
</li>
</ul>
<h2 id="一-创建型"><a href="#一-创建型" class="headerlink" title="一 . 创建型"></a>一 . 创建型</h2><h3 id="1-原型模式（Prototype）"><a href="#1-原型模式（Prototype）" class="headerlink" title="1. 原型模式（Prototype）"></a>1. 原型模式（Prototype）</h3><ul>
<li><p>使用原型实例指定要创建对象的类型，通过<strong>复制这个原型来创建新对象。</strong></p>
</li>
<li><p>简单来说，其实就是当需要创建一个指定对象的时候，我们刚好有这样一个对象，但是又不能直接使用这个对象，那么我就会<strong>clone</strong> 一个一模一样的对象，基本上这就是原型模型</p>
</li>
<li><p><strong>关键字： clone</strong></p>
</li>
</ul>
<p>这些场景可能派的上用场</p>
<ul>
<li>当new一个对象时，非常繁琐复杂时，可以使用原型模式来进行复制一个对象。即使需求的变更，这些对象需要作出调整，我们依然拥有比较稳定一致的接口创建对象。</li>
<li>需要提供数据对象，同时有需要避免外部对数据对象进行修改。</li>
</ul>
<p><strong>简单的UML类图</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200610/221312940.webp" alt="mark"></p>
<p><strong>角色：</strong></p>
<ul>
<li><code>client</code> ： 使用者</li>
<li><code>Prototype</code> : 接口（抽象类），声明具备clone 能力，例如Java中的<code>Cloneable</code> 接口</li>
<li><code>ConcretePrototype</code>： 具体的原型类</li>
</ul>
<p><strong>关于浅拷贝和深拷贝：</strong></p>
<ul>
<li><strong>浅拷贝</strong>：一个对象通过赋值的形式直接传递的其实是对象在内存中的内存地址</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ArrayList&lt;String&gt; a = <span class="keyword">new</span> ArrayList();</span><br><span class="line">ArrayList&lt;String&gt; b = a;</span><br><span class="line"><span class="comment">//当修改a时，b的值同样会被修改</span></span><br></pre></td></tr></table></figure>

<p>以上的代码就是浅拷贝，从某种角度来说，<strong>这种浅拷贝的方式并不合适在原型模式中使用，更多情况下我们需要一个不会影响原始对象的一个新对象，也就需要使用到深拷贝</strong></p>
<ul>
<li><strong>深拷贝</strong>：一个不会影响原始对象的一个新对象</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">ArrayList&lt;String&gt; a = <span class="keyword">new</span> ArrayList();</span><br><span class="line">ArrayList&lt;String&gt; b = a.clone();</span><br><span class="line"><span class="comment">//或者</span></span><br><span class="line">ArrayList&lt;String&gt; c = <span class="keyword">new</span> ArrayList(a);</span><br></pre></td></tr></table></figure>

<p>上面代码的</p>
<p><strong>第一种方式</strong>，是使用<code>Object</code> 类的<code>super.clone()</code>方法来使用实现拷贝的过程。</p>
<p><strong>第二种方式</strong>：使用a在创建了一个新的对象并且赋值给c ， 这样a和 c是两个值相同的两个对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ArrayList的clone()方法代码</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">clone</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        ArrayList&lt;?&gt; v = (ArrayList&lt;?&gt;) <span class="keyword">super</span>.clone();</span><br><span class="line">        v.elementData = Arrays.copyOf(elementData, size);</span><br><span class="line">        v.modCount = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">return</span> v;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (CloneNotSupportedException e) &#123;</span><br><span class="line">        <span class="comment">// this shouldn't happen, since we are Cloneable</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> InternalError(e);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>需要注意的是：通过实现<code>Cloneable</code>接口的原型模式在调用<code>clone()</code> 方法构造实例并不一定比new操作速度快，只有new 对象操作复杂且耗时成本较高的时候，clone方法才有效率上的提升。</p>
<p><strong>简单实现</strong></p>
<ol>
<li>Prototype</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Prototype</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> Prototype <span class="title">myclone</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="2">
<li>ConcretePrototype</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcretePrototype</span> <span class="keyword">extends</span> <span class="title">Prototype</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String filed;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ConcretePrototype</span><span class="params">(String filed)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.filed = filed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function">Prototype <span class="title">myclone</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ConcretePrototype(filed);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"ConcretePrototype&#123;"</span> +</span><br><span class="line">                <span class="string">"filed='"</span> + filed + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">'&#125;'</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="3">
<li>Client</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Prototype prototype = <span class="keyword">new</span> ConcretePrototype(<span class="string">"abc"</span>);</span><br><span class="line">        Prototype clone = prototype.myclone();</span><br><span class="line">        System.out.println(clone.toString());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>JDK</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29" target="_blank" rel="noopener">java.lang.Object#clone()</a></li>
</ul>
<h3 id="2-生成器（Builder）"><a href="#2-生成器（Builder）" class="headerlink" title="2. 生成器（Builder）"></a>2. 生成器（Builder）</h3><ul>
<li>封装一个对象的构造过程，并允许按照步骤构造。</li>
<li>Builder 模式也就是<strong>建造者模式</strong>，先说定义，<strong>将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示。</strong><ul>
<li>首先，将复杂对象的创建过程和部件分离出来，其实就是把创建过程和自身的部件解耦，使得构建过程和部件都可以自由扩展，两者之间的耦合度降到最低。</li>
<li>然后，再是相同的构建过程可以创建不同的表示，相同的组合也可以通过不同的部件创建出不同的对象。</li>
</ul>
</li>
</ul>
<p><strong>可能使用的场景</strong></p>
<ul>
<li>相同的方法，不同的执行顺序，产生不同的结果</li>
<li>多个部件（代码中就对应类的属性），都可以装配到同一个对象中，但是产生的结果又不相同的时候</li>
<li>初始化一个对象特别复杂，参数多且很多参数都有默认值的时候。</li>
</ul>
<p><strong>实现</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200611/114543747.webp" alt="mark"></p>
<ul>
<li>Product  : 抽象的产品类。</li>
<li>Builder : 抽象的Builder 类，规范产品的组件。</li>
<li>ConcreteBuilder  :  具体的Builder类，实现具体的组件过程。</li>
<li>Director : 统一组装的过程。</li>
</ul>
<ol>
<li>手机配置简化的抽象类，设置CPU、OS以、内存大小以及运存大小</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 这是一个手机配置简化的抽象类，</span></span><br><span class="line"><span class="comment">// 设置CPU、OS以、内存大小以及运存大小</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Phone</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> String mCPU;</span><br><span class="line">    <span class="keyword">protected</span> String mOS;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">int</span> mMemorySize;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">int</span> mStorageSize;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">setmCPU</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">setmOS</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setmMemorySize</span><span class="params">(<span class="keyword">int</span> mMemorySize)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.mMemorySize = mMemorySize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setmStorageSize</span><span class="params">(<span class="keyword">int</span> mStorageSize)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.mStorageSize = mStorageSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"Phone&#123;"</span> +</span><br><span class="line">                <span class="string">"mCPU='"</span> + mCPU + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", mOS='"</span> + mOS + <span class="string">'\''</span> +</span><br><span class="line">                <span class="string">", mMemorySize="</span> + mMemorySize +</span><br><span class="line">                <span class="string">", mStorageSize="</span> + mStorageSize +</span><br><span class="line">                <span class="string">'&#125;'</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="2">
<li>具体的IPhoneX的类，由于CPU和系统是固定，而内存和运存运存可选。</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 具体的IPhoneX的类，</span></span><br><span class="line"><span class="comment">// 由于CPU和系统是固定，而内存和运存运存可选。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">IPhoneX</span> <span class="keyword">extends</span> <span class="title">Phone</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">IPhoneX</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setmCPU</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mCPU = <span class="string">"A11"</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setmOS</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mOS = <span class="string">"iOS 11"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="3">
<li>抽象的Builder类，作为主要隔离作用的类，<strong>Phone的API每一个方法都有对应的builder方法，并且都返回自身来实现链式API</strong></li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 抽象的Builder类，作为主要隔离作用的类，</span></span><br><span class="line"><span class="comment">// Phone的API的每一个方法都有对应的build方法，并都返回自身来实现链式API。</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Builder</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//设置CPU</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> Builder <span class="title">buildCPU</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="comment">//设置系统</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> Builder <span class="title">buildOS</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="comment">//设置运存大小</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> Builder <span class="title">buildMemorySize</span><span class="params">(<span class="keyword">int</span> memorySize)</span></span>;</span><br><span class="line">    <span class="comment">//设置储存大小</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> Builder <span class="title">buildStorageSize</span><span class="params">(<span class="keyword">int</span> storageSize)</span></span>;</span><br><span class="line">    <span class="comment">//创建一个Phone对象</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> Phone <span class="title">create</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="4">
<li><code>IPhoneXBuilder</code>，具体的<code>Builder</code>类</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// IPhoneXBuilder，具体的Builder类</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">IPhoneXBuilder</span> <span class="keyword">extends</span> <span class="title">Builder</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> IPhoneX mIPhoneX = <span class="keyword">new</span> IPhoneX();</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> Builder <span class="title">buildCPU</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mIPhoneX.setmCPU();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Builder <span class="title">buildOS</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        mIPhoneX.setmOS();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Builder <span class="title">buildMemorySize</span><span class="params">(<span class="keyword">int</span> memorySize)</span> </span>&#123;</span><br><span class="line">        mIPhoneX.setmMemorySize(memorySize);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Builder <span class="title">buildStorageSize</span><span class="params">(<span class="keyword">int</span> storageSize)</span> </span>&#123;</span><br><span class="line">        mIPhoneX.setmStorageSize(storageSize);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注意一定要返回一个具体的对象</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Phone <span class="title">create</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mIPhoneX;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="5">
<li>Director：负责指挥phone的建造顺序</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Director类，负责构造Phone</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Director</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    Builder mBuilder = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Director</span><span class="params">(Builder Builder)</span> </span>&#123;</span><br><span class="line">        mBuilder = Builder;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">construct</span><span class="params">(<span class="keyword">int</span> memorySize,<span class="keyword">int</span> StorageSize)</span></span>&#123;</span><br><span class="line">        mBuilder.buildCPU().</span><br><span class="line">                buildOS().</span><br><span class="line">                buildMemorySize(memorySize).</span><br><span class="line">                buildStorageSize(StorageSize);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="6">
<li>客户端测试：</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Builder builder = <span class="keyword">new</span> IPhoneXBuilder();</span><br><span class="line"></span><br><span class="line">        Director director = <span class="keyword">new</span> Director(builder);</span><br><span class="line"></span><br><span class="line">        director.construct(<span class="number">6</span>,<span class="number">256</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(builder.create().toString());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>再来一个JDK中简易的实现 (StringBuilder)</strong></p>
<ol>
<li><code>AbstractStringBuilder</code></li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractStringBuilder</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">char</span>[] value;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">int</span> count;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AbstractStringBuilder</span><span class="params">(<span class="keyword">int</span> capacity)</span> </span>&#123;</span><br><span class="line">        count = <span class="number">0</span>;</span><br><span class="line">        value = <span class="keyword">new</span> <span class="keyword">char</span>[capacity];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> AbstractStringBuilder <span class="title">append</span><span class="params">(<span class="keyword">char</span> c)</span> </span>&#123;</span><br><span class="line">        ensureCapacityInternal(count + <span class="number">1</span>);</span><br><span class="line">        value[count++] = c;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureCapacityInternal</span><span class="params">(<span class="keyword">int</span> minimumCapacity)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// overflow-conscious code</span></span><br><span class="line">        <span class="keyword">if</span> (minimumCapacity - value.length &gt; <span class="number">0</span>)</span><br><span class="line">            expandCapacity(minimumCapacity);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">expandCapacity</span><span class="params">(<span class="keyword">int</span> minimumCapacity)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> newCapacity = value.length * <span class="number">2</span> + <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span> (newCapacity - minimumCapacity &lt; <span class="number">0</span>)</span><br><span class="line">            newCapacity = minimumCapacity;</span><br><span class="line">        <span class="keyword">if</span> (newCapacity &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (minimumCapacity &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> OutOfMemoryError();</span><br><span class="line">            newCapacity = Integer.MAX_VALUE;</span><br><span class="line">        &#125;</span><br><span class="line">        value = Arrays.copyOf(value, newCapacity);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="2">
<li><code>StringBuilder</code></li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StringBuilder</span> <span class="keyword">extends</span> <span class="title">AbstractStringBuilder</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">StringBuilder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(<span class="number">16</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// Create a copy, don't share the array</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> String(value, <span class="number">0</span>, count);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="3">
<li>Client:测试</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        StringBuilder sb = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> count = <span class="number">26</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; count; i++) &#123;</span><br><span class="line">            sb.append((<span class="keyword">char</span>) (<span class="string">'a'</span> + i));</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(sb.toString());  <span class="comment">// abcdefghijklmnopqrstuvwxyz</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>JDK中Builder模式的具体实现</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html" target="_blank" rel="noopener">java.lang.StringBuilder</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-" target="_blank" rel="noopener">java.nio.ByteBuffer</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-" target="_blank" rel="noopener">java.lang.StringBuffer</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html" target="_blank" rel="noopener">java.lang.Appendable</a></li>
<li><a href="https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder" target="_blank" rel="noopener">Apache Camel builders</a></li>
</ul>
<h3 id="3-工厂模式（Factory）"><a href="#3-工厂模式（Factory）" class="headerlink" title="3. 工厂模式（Factory）"></a>3. 工厂模式（Factory）</h3><ul>
<li>顾名思义，工厂模式就是生产产品的！</li>
<li>工厂模式(Factory Pattern) 提供了一种<strong>创建对象的最佳方式，属于创建型模式。</strong></li>
<li>在工厂模式中，创建对象时不会对客户端暴露创建逻辑，并且是通过一个共同的接口来指定新创建的对象。<strong>（绕开了new）</strong></li>
</ul>
<p><strong>大致描述：</strong></p>
<ul>
<li>将上述思想对应到code中，工厂模式需要做的就是帮助我们构建对象，因为构建对象的过程可能比较复杂，我们无法掌握（例如：无法直接new 出来）。这是对工厂模式的一个大致描述，接下来可以从实现方式来说明。</li>
</ul>
<h4 id="3-1-简单工厂模式（Simple-Factory）"><a href="#3-1-简单工厂模式（Simple-Factory）" class="headerlink" title="3.1 简单工厂模式（Simple Factory）"></a>3.1 简单工厂模式（Simple Factory）</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 简单工厂模式的大致模式</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Product</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 商品A</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteProductA</span> <span class="keyword">implements</span> <span class="title">Product</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 商品B</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteProductB</span> <span class="keyword">implements</span> <span class="title">Product</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 商品C</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteProductC</span> <span class="keyword">implements</span> <span class="title">Product</span> </span>&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>首先，简单工厂模式并没有被归纳到23中GOF设计模式中，其实可以理解为工厂模式的简单使用。</li>
<li><strong>一个工厂对象决定创建出哪一种类产品</strong>，而产品有不同的系列相互之间有些许差异。举个生产鞋的例子，先看UML图</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200613/092602806.webp" alt="mark"></p>
<p>大致可以分为三部分：工厂类，抽象产品类，具体的产品类</p>
<ul>
<li>Factory : 工厂类，生产shoe</li>
<li>Shoe : 抽象的产品类</li>
<li>SportShoe : 具体的产品，运动鞋</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 鞋的抽象类 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Shoe</span></span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 运动鞋 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SportShoe</span> <span class="keyword">extends</span> <span class="title">Shoe</span></span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 工厂类 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShoeFactory</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> Shoe <span class="title">produceShoe</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> SportShoe();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>以上就是最简单的工厂，但是一个只生产运动鞋的工厂，老板想去赚更多的钱，要求添加生成<code>HighHeeledShoe</code>(高跟鞋)。我们需要对<code>ShoeFactory</code>的设计进行修改，如图：</p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200613/092542993.webp" alt="mark"></p>
<p>改造后的代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在produceShoe()的函数中添加了type参数，</span></span><br><span class="line"><span class="comment">// 表示Shoe的不同类型，以此来生产不同的鞋子。我们开始改造ShoeFactory类</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/** 高跟鞋 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HighHeeledShoe</span> <span class="keyword">extends</span> <span class="title">Shoe</span></span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 修改后的工厂类 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShoeFactory</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> Shoe <span class="title">produceShoe</span><span class="params">(String type)</span></span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"sport"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> SportShoe();</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"highHeeled"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> HighHeeledShoe();</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> Shoe();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>这样我们可以根据给定的不同类型生产对应鞋子了。这里看到代码可能会有两个问题</p>
<ul>
<li>第一：每次添加一个品牌需要加一个case(虽然现实中是很正常的逻辑。但是在代码层面并不优雅)。同时，<strong>简单工厂模式违反了开闭原则，即对扩展开放，对修改关闭；因为增加了具体产品，就需要修改对应工厂类代码；</strong></li>
<li>第二：调用<code>produceShoe（）</code>的时候，我们还需要去创建一个Factory对象并进行控制管理<ul>
<li><strong>直接new出<code>Factory</code>的对象</strong>（如<code>return new SportShoe();</code>），我们就必须自己控制工厂类的构造和生成，同时我们也需要非常清楚工厂的构造函数（比如构造函数有多少个参数，输入参数时有什么条件等等），还需要知道工厂的内部细节，一旦工厂扩展或者改变了，就很可能不知道怎么调用了，对于调用者来说无疑会是噩梦。</li>
</ul>
</li>
</ul>
<p><strong>优化问题：</strong></p>
<ul>
<li>先优化第一个问题：从技术的层面我们不使用<code>swith case</code> 这种形式来实现不同类型的对象创建，我们<strong>使用反射就可以实现优化（编译期到运行期）</strong></li>
<li>对于第二个问题：最直接的解决方案就是我们直接调用生产鞋子的方法，直接告诉工厂再由工厂生产，这样我们就不需要创建工厂。（那就是给<code>produceShoe()</code>函数添加<code>static</code>关键字就可以解决问题了）</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 优化后的工厂类 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShoeFactory</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 根据类型生产鞋子</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> cls</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Shoe <span class="title">produceShoe</span><span class="params">(Class&lt;? extends Shoe&gt; cls)</span></span>&#123;</span><br><span class="line">        Shoe shoe = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            shoe = (Shoe) Class.forName(cls.getName()).newInstance();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InstantiationException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> shoe;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>这样一来问题就解决了</strong></p>
<ul>
<li>用静态方法则完全不用关心如何构造对象，我们需要关心工厂的构造细节，即使工厂内部发生变化也不需要关心</li>
<li>简单工厂模式主要适用于创建抽象子类的业务相同但具体实现不同的对象的情况。</li>
</ul>
<h4 id="3-2-工厂方法模式"><a href="#3-2-工厂方法模式" class="headerlink" title="3.2 工厂方法模式"></a>3.2 工厂方法模式</h4><ul>
<li>实际上，制造鞋子的厂商一定不会只有一家，肯定存在多家竞争的关系，所以有更多的制造不同鞋子的工厂，<code>produceShoe</code>函数就可以抽象出来，不同的<code>Factory</code>子类根据自己的需求去实现。</li>
<li>例如下图的<code>NikeFactory</code>和<code>DaphneFactory</code>类</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200613/112053054.webp" alt="mark"></p>
<ul>
<li>相对于简单工厂，<strong>工厂方法的区别就在于工厂类分类抽象工厂和具体的实现工厂类。</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 抽象工厂类 */</span></span><br><span class="line">pulibc <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Factory</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> Shoe <span class="title">produceShoe</span><span class="params">(String type)</span>；</span></span><br><span class="line"><span class="function">&#125;</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="comment">/** Nike工厂类 */</span></span></span><br><span class="line"><span class="function"><span class="keyword">public</span> class NikeFactory extends Factory </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Shoe <span class="title">produceShoe</span><span class="params">(String type)</span></span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"sport"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> SportShoe();</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"highHeeled"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> HighHeeledShoe();</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> Shoe();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** Daphne工厂类 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DaphneFactory</span> <span class="keyword">extends</span> <span class="title">Factory</span></span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Shoe <span class="title">produceShoe</span><span class="params">(String type)</span></span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"sport"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> SportShoe();</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"highHeeled"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> HighHeeledShoe();</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> Shoe();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>使用的时候就很简单了，只需要调用对应的工厂，选择客户需要的类型就可以获得相应的产品了，这样不同的制造商就能分别生产不同的鞋子。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Factory daphneFactory = <span class="keyword">new</span> DaphneFactory();</span><br><span class="line">daphneFactory.produceShoe(<span class="string">"highHeeled"</span>);</span><br><span class="line"></span><br><span class="line">Factory nikeFactory = <span class="keyword">new</span> NikeFactory();</span><br><span class="line">nikeFactory.produceShoe(<span class="string">"sport"</span>);</span><br></pre></td></tr></table></figure>



<p>在工厂的环节采用抽象类的形式实现，其实也可以将<code>produceShoe</code>抽象成<code>IProduceShoe</code>接口，当然，这也是我的个人理解。其实觉得抽象类或者接口使用其中一个就可以了，不必要同时使用；但也不是绝对的，这个需要根据具体的情况而定。</p>
<p><strong>对应JDK中：</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--" target="_blank" rel="noopener">java.util.Calendar</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-" target="_blank" rel="noopener">java.util.ResourceBundle</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--" target="_blank" rel="noopener">java.text.NumberFormat</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-" target="_blank" rel="noopener">java.nio.charset.Charset</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-" target="_blank" rel="noopener">java.net.URLStreamHandlerFactory</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-" target="_blank" rel="noopener">java.util.EnumSet</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--" target="_blank" rel="noopener">javax.xml.bind.JAXBContext</a></li>
</ul>
<p><strong>通用形式：</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Factory</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">public</span> Product <span class="title">factoryMethod</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doSomething</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Product product = factoryMethod();</span><br><span class="line">        <span class="comment">// do something with the product</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteFactory</span> <span class="keyword">extends</span> <span class="title">Factory</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Product <span class="title">factoryMethod</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ConcreteProduct();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteFactory1</span> <span class="keyword">extends</span> <span class="title">Factory</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Product <span class="title">factoryMethod</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ConcreteProduct1();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteFactory2</span> <span class="keyword">extends</span> <span class="title">Factory</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Product <span class="title">factoryMethod</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ConcreteProduct2();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="3-3-抽象工厂模式（Abstract-Factory）"><a href="#3-3-抽象工厂模式（Abstract-Factory）" class="headerlink" title="3.3 抽象工厂模式（Abstract Factory）"></a>3.3 抽象工厂模式（Abstract Factory）</h4><ul>
<li>生产鞋子的厂商不一定只会生产衣服<code>cloth</code>,我们需要为了工厂添加制造衣服的这条生产线。为了工厂添加生产衣服后的UML图如下：</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200613/113418278.webp" alt="mark"></p>
<ul>
<li><p><strong>将<code>ShoeFactory</code>和<code>ClothFactory</code>抽象成接口（原因：Java无法多继承，所以使用Interface）</strong></p>
</li>
<li><p>需要对应的产品就要实现工厂对应的接口。</p>
</li>
<li><p>添加<code>NikeFactory</code>中的生产衣服的功能<code>produceCloth</code>,这样也就是实现<code>NikeFactory</code>可以生产多种产品。</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NikeFactory</span> <span class="keyword">extends</span> <span class="title">Factory</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Shoe <span class="title">produceShoe</span><span class="params">(String type)</span></span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"sport"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> SportShoe();</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"highHeeled"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> HighHeeledShoe();</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> Shoe();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//新增方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Cloth <span class="title">produceCloth</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"sport"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> SportCloth();</span><br><span class="line">            <span class="keyword">case</span> <span class="string">"skirt"</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> Skirt();</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">new</span> Shoe();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><p><strong>当你需要创建一些对象家族时候，抽象工厂也是不错的选择，因为它可以一次性创建多个对象。</strong></p>
</li>
<li><p>抽象工厂十分强大灵活（至少比前两种都要好），可以用<strong>多个抽象子类完成复杂的需求</strong>，同时保证了外界接触不到任何类型的具体产品类型，某种意思上很符合开闭原则 。</p>
</li>
<li><p>当然，缺点也显而易见，模式比较庞大，从UML图就能看出来。</p>
</li>
</ul>
<h4 id="3-4-小结"><a href="#3-4-小结" class="headerlink" title="3.4 小结"></a>3.4 小结</h4><ul>
<li>简单工厂<strong>只能有一个工厂生产相同类型的产品</strong></li>
<li>工厂方法模式<strong>只能生产一系列的产品，可以同时有不同的工厂去实现。</strong></li>
<li>抽象工厂模式通过<strong>实现不同的抽象方法可以生产出多个系列的产品。</strong></li>
</ul>
<h3 id="4-单例模式"><a href="#4-单例模式" class="headerlink" title="4. 单例模式"></a>4. 单例模式</h3><ul>
<li>单例模式是应用最广泛的模式之一，定义就是<strong>单例对象的类必须保证只有一个实例存在</strong></li>
<li>单例模式适用于创建一个对象需要消耗过多资源的情况，例如数据库等资源是需要考虑使用的。</li>
</ul>
<p>实现单例模式关键点如下：</p>
<ul>
<li><strong>构造函数私有化（不会让你有机会再创建一个对象）</strong></li>
<li><strong>通过一个静态方法或枚举（后面会有举例）返回单例类对象</strong></li>
<li>确保单例类的对象有且只有一个,尤其是多线程的环境下(难点)</li>
<li>确保单例类的对象在反序列化的时候不会重新构建对象</li>
</ul>
<p><img src="https://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200313101430.png" alt=""></p>
<h4 id="4-1-饿汉式-线程安全"><a href="#4-1-饿汉式-线程安全" class="headerlink" title="4.1 饿汉式-线程安全"></a>4.1 饿汉式-线程安全</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 饿汉式实现</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 声明对象的时候就 已经初始化</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Singleton instance = <span class="keyword">new</span> Singleton();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 私有化构造器</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Singleton</span><span class="params">()</span></span>&#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 共有静态方法，对外暴露获取的途径(Getter方法)</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title">getInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="4-2-懒汉式-线程不安全"><a href="#4-2-懒汉式-线程不安全" class="headerlink" title="4.2 懒汉式-线程不安全"></a>4.2 懒汉式-线程不安全</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 2. 懒汉式实现</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 声明对象的时候不进行初始化</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Singleton instance = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有化构造器</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Singleton</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Getter方法加锁保证线程安全</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">synchronized</span> Singleton <span class="title">getInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 进入是检查有没有被创建</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">null</span> == instance)&#123;</span><br><span class="line">            instance = <span class="keyword">new</span> Singleton();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>懒汉式与饿汉式不同的地方是单例对象初始化的时机,实现了懒加载</li>
<li>同时<code>getInstance()</code>方法前添加了<code>synchronized</code>关键字,以此来确保多线程环境下单例模式的唯一性.(相对的,每次调用<code>getInstance()</code>方法都是会进行同步的,也是懒汉式最大的问题)</li>
</ul>
<h4 id="4-3-懒汉式-DCL-线程不安全"><a href="#4-3-懒汉式-DCL-线程不安全" class="headerlink" title="4.3 懒汉式(DCL)-线程不安全"></a>4.3 懒汉式(DCL)-线程不安全</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 3. DCL</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 声明对象的时候不进行初始化</span></span><br><span class="line">    <span class="comment">// volatile作用：避免指令的重排，保证原子性</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> Singleton instance = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私有化构造器</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Singleton</span><span class="params">()</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 双重检测锁</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title">getInstance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">null</span> == instance)&#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (Singleton<span class="class">.<span class="keyword">class</span>)</span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="keyword">null</span> == instance)&#123;</span><br><span class="line">                     instance = <span class="keyword">new</span> Singleton();  <span class="comment">// 造成问题的关键</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 返回对象实例</span></span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><p>可以看到<code>getInstance()</code>方法对instance进行了两次判空,</p>
<ul>
<li>第一次判断是为了判断不必要的同步</li>
<li>第二次是为了判断null的情况下创建实例</li>
</ul>
</li>
<li><p>同时<code>instance</code>对象前面还添加了 <code>volatile</code>关键字,如果不使用<code>volatile</code>是无法保证<code>instance</code>的原子性的</p>
<ul>
<li><code>instance = new Singleton();</code>这句代码不是一个原子性操作</li>
<li><strong>这句代码最终会被编译成多条汇编指令，大致做了3件事：</strong></li>
<li><ol>
<li><strong>给Singleton的实例分配内存空间</strong></li>
<li><strong>调用<code>Singleton()</code>的构造函数,初始化成员字段</strong></li>
<li><strong>指向分配的内存空间(此时<code>instance</code>不为null)**</strong></li>
</ol>
</li>
</ul>
</li>
<li><p>由于JMM允许指令重排来保证代码效率,所以上述第二点和第三点的顺序无法保证,也就是说执行顺序可能是1-2-3或者是1-3-2。</p>
<ul>
<li>如果是1-3-2, instance 在 3 步骤的时候就已经是非空了,所以2步骤没有执行,那么另外一个线程会取走一个还没初始化完毕的实例,导致DCL失效</li>
<li>在JDK1.5之后，调整了JVM，具体化了<code>volatile</code>关键字。然，<code>volatile</code>或多或少会影响到性能，考虑到正确性这点性能的牺牲还是值得的。</li>
</ul>
</li>
<li><p><strong>DCL模式能够在绝大多数场景下保证单例对象的唯一性，资源利用率高，只有第一次加载时反应稍慢，一般能够满足需求</strong></p>
</li>
</ul>
<h4 id="4-4-静态内部类"><a href="#4-4-静态内部类" class="headerlink" title="4.4 静态内部类"></a>4.4 静态内部类</h4><ul>
<li>懒汉式的改进</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 4 . 静态内部类实现</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Singleton</span></span>&#123;</span><br><span class="line">   <span class="comment">// 1. 私有化构造器</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Singleton</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2. 静态内部类</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">SingletonHolder</span></span>&#123;</span><br><span class="line">        <span class="comment">// final：保证线程中只有一个存在</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Singleton instance = <span class="keyword">new</span> Singleton();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3. 只有调用的时候才会加载</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title">getInstance</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> SingletonHolder.instance;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li><p>当第一次加载<code>Singleton</code>类时并不会初始化<code>instance</code>，只有在第一次调用<code>getInstance()</code>方法是回初始化。</p>
</li>
<li><p>第一次调用<code>getInstance()</code>方法导致虚拟机加载<code>SingletonHolder</code>类，这种方式嫩确保线程安全，也能确保单例对象的唯一性，</p>
</li>
<li><p>同时也延迟了单例对象的实例化，所以推荐使用这种实现方式。</p>
</li>
</ul>
<h4 id="4-5-枚举单例-线程安全"><a href="#4-5-枚举单例-线程安全" class="headerlink" title="4.5 枚举单例-线程安全"></a>4.5 枚举单例-线程安全</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//  5.枚举单例模式</span></span><br><span class="line"><span class="keyword">public</span> enmu SingletonEnum &#123;</span><br><span class="line">    INSTANCE;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>测试:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 5. 枚举单例</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> Singleton&#123;</span><br><span class="line">    INSTANCE; <span class="comment">//纯天然单例模式</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Singleton instance1 = Singleton.INSTANCE;</span><br><span class="line">        Singleton instance2 = Singleton.INSTANCE;</span><br><span class="line">        System.out.println(instance1 == instance2);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>就是这么简单粗暴，其实最大优点在于关键点的第4点，即是反序列化也不会重新生成新的实例。</li>
</ul>
<h4 id="4-6-总结"><a href="#4-6-总结" class="headerlink" title="4.6 总结"></a>4.6 总结</h4><p>不管哪种形式实现单例模式，核心原理都是那四个关键点，具体选择哪种实现方式取决于项目本身以及具体的开发环境等等。</p>
<p>而对于客户端来说通常没有高并发的情况，推荐使用DCL模式或者是静态内部类的方式实现。</p>
<p>优点</p>
<ul>
<li>只存在一个实例，减少了内存开支，减少了系统的性能开销</li>
<li>避免对资源的多重占用</li>
<li>全局的访问点，优化和共享资源访问</li>
</ul>
<p>缺点</p>
<ul>
<li>没有接口，难扩展，只能修改代码</li>
<li>如果持有Context容易导致内存泄露（需要传递Context的话最好是Application Context）</li>
</ul>
<p><strong>JDK实现:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29" target="_blank" rel="noopener">java.lang.Runtime#getRuntime()</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--" target="_blank" rel="noopener">java.awt.Desktop#getDesktop()</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--" target="_blank" rel="noopener">java.lang.System#getSecurityManager()</a></li>
</ul>
<h2 id="二-行为型"><a href="#二-行为型" class="headerlink" title="二. 行为型"></a>二. 行为型</h2><h4 id="1-模板方法模式（Template-Method）"><a href="#1-模板方法模式（Template-Method）" class="headerlink" title="1. 模板方法模式（Template Method）"></a>1. 模板方法模式（Template Method）</h4><ul>
<li>动机：对于一项任务，常常有<strong>稳定的整体操作结构</strong>，但各个子步骤却又很多改变的需求，或者需要子步骤的晚期实现（<strong>延迟到子类去实现</strong>）。</li>
<li>Template Method 使得子类可以复用一个算法的结构（Override 重写）该算法的某些特定步骤。</li>
<li>不要调用我，让我来调用你，实现晚绑定机制，这也就是控制反转的思想。</li>
<li>声明成 <code>protected</code> ,因为具体步骤在流程中才有意义。</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200615/161657528.png" alt="mark"></p>
<ul>
<li><strong>AbstractClass : 稳定的骨架（里面有具体的方法和需要被重写的方法）</strong></li>
<li><strong>ContreteClass : 具体的重写方法</strong></li>
</ul>
<ul>
<li>具体实现（举例）</li>
</ul>
<p>冲咖啡和冲茶都有类似的流程，但是某些步骤会有点不一样，要求复用那些相同步骤的代码。</p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200615/162632540.png" alt="mark"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">AbstractClass</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 骨架流程</span></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">prepareRecipe</span><span class="params">()</span></span>&#123;</span><br><span class="line">        boilWater();</span><br><span class="line">        brew();</span><br><span class="line">        pourIncoup();</span><br><span class="line">        addCondiments();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 待重写的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">brew</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 待重写的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">addCondiments</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">pourIncoup</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"倒进杯子"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">boilWater</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"倒水"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Coffee</span> <span class="keyword">extends</span> <span class="title">AbstractClass</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">brew</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"倒咖啡"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addCondiments</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"加入咖啡粉"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Tea</span> <span class="keyword">extends</span> <span class="title">AbstractClass</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">brew</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"倒茶"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">addCondiments</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"加入茶叶"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        AbstractClass coffee = <span class="keyword">new</span> Coffee();</span><br><span class="line">        coffee.prepareRecipe();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"========"</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        AbstractClass tea = <span class="keyword">new</span> Tea();</span><br><span class="line">        tea.prepareRecipe();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>结果显示：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">倒水</span><br><span class="line">倒咖啡</span><br><span class="line">倒进杯子</span><br><span class="line">加入咖啡粉</span><br><span class="line">========</span><br><span class="line">倒水</span><br><span class="line">倒茶</span><br><span class="line">倒进杯子</span><br><span class="line">加入茶叶</span><br></pre></td></tr></table></figure>



<p><strong>JDK中实现：</strong></p>
<ul>
<li>java.util.Collections#sort()</li>
<li>java.io.InputStream#skip()</li>
<li>java.io.InputStream#read()</li>
<li>java.util.AbstractList#indexOf()</li>
</ul>
<h4 id="2-策略模式（Strategy）"><a href="#2-策略模式（Strategy）" class="headerlink" title="2. 策略模式（Strategy）"></a>2. 策略模式（Strategy）</h4><ul>
<li><p>定义：针对同一个算法的问题多种处理方式，仅仅是具体的行为有差别的时候</p>
</li>
<li><p>在工厂模式中，为了创建不同的产品使用了<code>switch case</code>（或<code>if else</code>）的形式的代码，这样违背了<strong>开闭原则：对扩展开放，对修改封闭</strong> 。</p>
</li>
<li><p>服务端的性能负担会随着条件判断的增加而增加，而本文的<strong>策略模式</strong>可以较好的解决这个问题</p>
</li>
</ul>
<p><strong>定义：定义了一系列的算法，把它们一个个封装起来，并且是他们可以相互替换。策略模式让算法独立于它的使用者之外，可以自由修改。</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200623/091054383.webp" alt="mark"></p>
<p>来看看UML类图，图中主要有三个部分组成</p>
<ul>
<li>Context: 上下文环境，持有<code>Strategy</code> 引用</li>
<li>Strategy: 抽象的策略（接口或者抽象类）</li>
<li>ConcreteStrategy: 具体的实现策略，实现了具体的算法（<strong>一般是工厂模式的具体实现</strong>）</li>
</ul>
<p><strong>注意：</strong></p>
<p><strong><code>Strategy</code>是使用接口还是抽象类</strong></p>
<ul>
<li>取决于一系列的策略中是否有共同的属性或者方法，如果没有，使用接口更加灵活方便</li>
<li>反之，使用抽象类，抽象类中便可存放公共的属性以及方法。</li>
</ul>
<p><strong>原始待修改代码：</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 待修改的代码</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LetGo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String MODE_AIRPLAN = <span class="string">"airPlan"</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String MODE_TRAVEL = <span class="string">"travel"</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String MODE_CAR = <span class="string">"car"</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">printSpend</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"出行花费："</span> + needSpend(MODE_AIRPLAN));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">needSpend</span><span class="params">(String model)</span></span>&#123;</span><br><span class="line">        <span class="keyword">switch</span> (model)&#123;</span><br><span class="line">            <span class="keyword">case</span> MODE_AIRPLAN:</span><br><span class="line">                <span class="keyword">return</span> <span class="number">1400</span>;</span><br><span class="line">            <span class="keyword">case</span> MODE_TRAVEL:</span><br><span class="line">                <span class="keyword">return</span> <span class="number">500</span>;</span><br><span class="line">            <span class="keyword">case</span> MODE_CAR:</span><br><span class="line">                <span class="keyword">return</span> <span class="number">2400</span>;</span><br><span class="line">                <span class="comment">// 异常值</span></span><br><span class="line">                <span class="keyword">default</span>:</span><br><span class="line">                    <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        LetGo letGo = <span class="keyword">new</span> LetGo();</span><br><span class="line">        letGo.printSpend();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如同前文所说的，当需要添加可选的出行方案时，我们不得不去修改<code>needSpend()</code>函数中的<code>switch case</code>来达到目的；然而这样并不利于后期的维护。接下来，试着使用策略模式来使实现这个简单的出行案例。</p>
<p><strong>策略模式实现：</strong></p>
<ul>
<li>既然，出现是可选的策略，我们可以先抽象除一个出行策略的的接口，包含<code>needSpend()</code>方法，返回出行花费的方法</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ITravelStrategy</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">needSpend</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>接着，实现各种出行方案的实现类</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 飞机出行 */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AirPlanStrategy</span> <span class="keyword">implements</span> <span class="title">ITravelStrategy</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">needSpend</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1400</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 火车出行 */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TrainStrategy</span>  <span class="keyword">implements</span> <span class="title">ITravelStrategy</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">needSpend</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">500</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 自驾出行 */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CarStrategy</span>  <span class="keyword">implements</span> <span class="title">ITravelStrategy</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">needSpend</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">2400</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样我们需要的策略就都已经完成了，就等着我们选一种方案，看看所需要的花费。</p>
<ul>
<li>我们创建一个类，和<strong><code>Strategy</code>组合</strong>使用来获取各种出行方的花费，并在<code>printSpend()</code>方法中打印出所需要的花费。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LetGoII</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 组合的思想</span></span><br><span class="line">    ITravelStrategy iTravelStrategy;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LetGoII</span><span class="params">(ITravelStrategy iTravelStrategy)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.iTravelStrategy = iTravelStrategy;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">printSpend</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"出行花费："</span> + iTravelStrategy.needSpend());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>测试：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    LetGoII letGoII = <span class="keyword">new</span> LetGoII(<span class="keyword">new</span> AirPlanStrategy());</span><br><span class="line">    letGoII.printSpend();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>以上的代码就可以完成自驾游花费的输出，使用了策略模式。</p>
<p>现在代码是不是比之前使用<code>switch case</code>实现的代码结构更加清晰、简洁</p>
<p>如果要实现更多的出行方式，只需要再实现<code>ITravelStrategy</code>接口,替换掉传入的<code>LetGo</code> 构造器的参数即可。</p>
<p><strong>总结：</strong></p>
<ul>
<li><strong>策略模式，其实可以简单地理解成，将过多的<code>switch case</code>中<code>case</code>的代码封装成一个个具有共性的对象，需要什么我们就直接使用什么；</strong></li>
<li><strong>对于这种共性的实现就利用<code>interface</code>或者是抽象类来实现。这从对代码的封装以及解耦的角度来理解，可能会更加容易理解。</strong></li>
</ul>
<p><strong>使用场景：</strong></p>
<ul>
<li>针对同一个算法的问题多种处理方式，仅仅是具体的行为有差别的时候</li>
<li>出现同一抽象类的多个子类，而有需要使用<code>switch case</code>或<code>if else</code>来选择具体子类时</li>
</ul>
<h4 id="3-观察者模式（Obersver）"><a href="#3-观察者模式（Obersver）" class="headerlink" title="3. 观察者模式（Obersver）"></a>3. 观察者模式（Obersver）</h4><ul>
<li>观察者模式是使用频率非常高的模式了，它定义了对象间一种一对多的关系，使得每当一个对象改变状态，则所有依赖它的对象都会收到通知，并且自动更新。</li>
<li>例如：Java中的监听器Listener用的就是观察者模式。</li>
</ul>
<p><strong>UML类图：</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200624/094333516.png" alt="mark"></p>
<ul>
<li>Subject: 具有注册和移除观察者，并且通知观察者的功能，主体是通过某种数据结构（可能是列表）来维护一张观察者列表实现这些操作的。</li>
<li>观察者（Observer）的注册功能需要调用主体的registerObserver() 方法。</li>
</ul>
<p><strong>实现：</strong></p>
<ul>
<li>开发技术周报，每周会更新一些内容，但是不知道具体的更新时间，又想第一时间阅读更新内容。</li>
<li>难道要一直按住F5等它更新么？那估计F5烂了可能都没有更新。其实我们只需要简单的订阅一下就好，当有新的内容更新的时候，会发邮件到你订阅的邮箱中。</li>
</ul>
<p>上述例子中：</p>
<ul>
<li>订阅者就是观察者，技术周报就是被观察者</li>
</ul>
<p><strong>代码实现：</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.Observable;</span><br><span class="line"><span class="keyword">import</span> java.util.Observer;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 观察者：程序员</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Coder</span> <span class="keyword">implements</span> <span class="title">Observer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Coder</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">update</span><span class="params">(Observable o, Object arg)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"收到的更新内容为"</span> + o);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 被观察者：主体（开发周报）</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Weekly</span> <span class="keyword">extends</span> <span class="title">Observable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 一对多的通知</span></span><br><span class="line">    <span class="keyword">private</span> ArrayList&lt;Observer&gt; observers = <span class="keyword">new</span> ArrayList&lt;Observer&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 增加一个观察者</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">subscribe</span><span class="params">(Observer observer)</span> </span>&#123;</span><br><span class="line">        observers.add(observer);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除一个观察者</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unsubscribe</span><span class="params">(Observer observer)</span> </span>&#123;</span><br><span class="line">        observers.remove(observer);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">notifyObservers</span><span class="params">(Object obj)</span> </span>&#123;</span><br><span class="line">        observers.forEach(observer -&gt; observer.update(<span class="string">"数据更新了"</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>JDK中自带的观察者模式的类</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200624/103921192.png" alt="mark"></p>
<ul>
<li>在<code>java.util</code> 包中内置了<code>Observer</code> 和 <code>observable</code>类,同时<code>Observable</code>类实现了注册和反注册等方法，使用起来方便很多。可见观察者模式是非常重要的。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Observable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> changed = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">private</span> Vector&lt;java.util.Observer&gt; obs;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Observable</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        obs = <span class="keyword">new</span> Vector&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注册</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">addObserver</span><span class="params">(Observer o)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (o == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">        <span class="keyword">if</span> (!obs.contains(o)) &#123;</span><br><span class="line">            obs.addElement(o);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 反注册</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">deleteObserver</span><span class="params">(Observer o)</span> </span>&#123;</span><br><span class="line">        obs.removeElement(o);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 反注册所有观察者</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">deleteObservers</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        obs.removeAllElements();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 通知更新</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">notifyObservers</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        notifyObservers(<span class="keyword">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 通知更新</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">notifyObservers</span><span class="params">(Object arg)</span> </span>&#123;</span><br><span class="line">        Object[] arrLocal;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!changed)</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            arrLocal = obs.toArray();</span><br><span class="line">            clearChanged();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = arrLocal.length-<span class="number">1</span>; i&gt;=<span class="number">0</span>; i--)</span><br><span class="line">            ((Observer)arrLocal[i]).update(<span class="keyword">this</span>, arg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">setChanged</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        changed = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">clearChanged</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        changed = <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">boolean</span> <span class="title">hasChanged</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> changed;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">int</span> <span class="title">countObservers</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> obs.size();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><code>Observer</code>接口则是比较简单的代码，<code>update()</code>的参数中除了可以传递数据意外，还提供了被观察者的引用对象。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"> */</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Observer</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * This method is called whenever the observed object is changed. An</span></span><br><span class="line"><span class="comment">     * application calls an &lt;tt&gt;Observable&lt;/tt&gt; object's</span></span><br><span class="line"><span class="comment">     * &lt;code&gt;notifyObservers&lt;/code&gt; method to have all the object's</span></span><br><span class="line"><span class="comment">     * observers notified of the change.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>   o     the observable object.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span>   arg   an argument passed to the &lt;code&gt;notifyObservers&lt;/code&gt;</span></span><br><span class="line"><span class="comment">     *                 method.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">update</span><span class="params">(Observable o, Object arg)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<p><strong>JDK中的实现：</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html" target="_blank" rel="noopener">java.util.Observer</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/EventListener.html" target="_blank" rel="noopener">java.util.EventListener</a></li>
<li><a href="http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html" target="_blank" rel="noopener">javax.servlet.http.HttpSessionBindingListener</a></li>
<li><a href="https://github.com/ReactiveX/RxJava" target="_blank" rel="noopener">RxJava</a></li>
</ul>
<h4 id="4-中介者模式（Mediator）"><a href="#4-中介者模式（Mediator）" class="headerlink" title="4 . 中介者模式（Mediator）"></a>4 . 中介者模式（Mediator）</h4><ul>
<li>中介者模式（Mediator Pattern） , 使用一个中介对象封装一系列对象交互，使得个对象之间没有明显的交互，并且能够独立的改变对象之间的交互（可能说的有点绕<strong>T_T</strong>）</li>
<li>看看下面这张<strong>UML类图</strong>，就能理解了。</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200701/091636157.webp" alt="mark"></p>
<ul>
<li>其实和门面模式（Facade）有异曲同工之处，Facade解决的是系统内外的耦合性问题，而Mediator解决的是系统内的对象间的耦合问题。</li>
</ul>
<p><strong>举个例子：</strong></p>
<ul>
<li>简单的说就是，中介者对象聚合了对象的交互，其他的对象都是通过中介者对象进行交互，没有直接的交互。</li>
<li>这个可以想象一下租房，中介手里有房东的房子，你找中介租房，中介就是你和房东之间的那个中介对象。（代理模式也是如此）</li>
</ul>
<blockquote>
<p>虽然跳过中介直接找房东更加便宜，但是相对的，中介起的作用也简化和房东扯皮的一些过程</p>
</blockquote>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200701/092158293.webp" alt="mark"></p>
<ul>
<li>Meadiator:  抽象中介者，定义了同事对象到中介者对象的接口。</li>
<li>ConcreteMediator： 具体的中介者角色，继承抽象中介者，实现了父类定义的方法，负责具体的同事对象之间的交互</li>
<li>Colleague : 抽象同事类,定义了中介者对象接口,只于中介者交互.不与其他同事对象交互.</li>
<li>ConcreteColleagueA/B:具体的同事类,继承抽象的同事类,每个具体的同事类都知道本身小范围内的行为,不知道自己大范围内的目的.</li>
</ul>
<p>其实这样描述,有没有觉得想MVC模式里面的Controller(当然在Controller里面的代码可能不会像中介者这样降低View 之间的耦合),也是起到Model 和 View 聚合的一个作用,使得Model 和 View的耦合性降低.</p>
<p><strong>代码实现:</strong></p>
<ul>
<li>直接实现房客找中介联系房东的过程，首先是抽象中介者</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Mediator</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span>  <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">constact</span><span class="params">(Person person,String message)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>然后是抽象的同事类</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> String name;</span><br><span class="line">    <span class="keyword">protected</span> Mediator mediator;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Person</span><span class="params">(String name,Mediator mediator)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.mediator = mediator;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>接下来就是创建两个具体的同事类了，也就是<code>HouseOwner</code>房东和<code>Tenant</code>租客</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HouseOwner</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">HouseOwner</span><span class="params">(String name, Mediator mediator)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name, mediator);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@desc</span> 与中介者联系</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">constact</span><span class="params">(String message)</span></span>&#123;</span><br><span class="line">        mediator.constact(<span class="keyword">this</span>,message);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@desc</span> 获取信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getMessage</span><span class="params">(String message)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"房主:"</span> + name +<span class="string">",获得信息："</span> + message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Tenant</span> <span class="keyword">extends</span> <span class="title">Person</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Tenant</span><span class="params">(String name, Mediator mediator)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name, mediator);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@desc</span> 与中介者联系</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">constact</span><span class="params">(String message)</span></span>&#123;</span><br><span class="line">        mediator.constact(<span class="keyword">this</span>,message);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@desc</span> 获取信息</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> message</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getMessage</span><span class="params">(String message)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"租房者:"</span> + name +<span class="string">",获得信息："</span> + message);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>接下来就需要具体的中介者，将房东和租客聚合起来</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MediatorStructure</span> <span class="keyword">extends</span> <span class="title">Mediator</span> </span>&#123;</span><br><span class="line">    <span class="comment">//首先中介结构必须知道所有房主和租房者的信息</span></span><br><span class="line">    <span class="keyword">private</span> HouseOwner houseOwner;</span><br><span class="line">    <span class="keyword">private</span> Tenant tenant;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> HouseOwner <span class="title">getHouseOwner</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> houseOwner;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setHouseOwner</span><span class="params">(HouseOwner houseOwner)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.houseOwner = houseOwner;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Tenant <span class="title">getTenant</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> tenant;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTenant</span><span class="params">(Tenant tenant)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.tenant = tenant;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">constact</span><span class="params">(Person person, String message)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (person == houseOwner) &#123;</span><br><span class="line">            <span class="comment">//如果是房主，则租房者获得信息</span></span><br><span class="line">            tenant.getMessage(message);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//反正则是房主获得信息</span></span><br><span class="line">            houseOwner.getMessage(message);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>测试类</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//一个房主、一个租房者、一个中介机构</span></span><br><span class="line">        MediatorStructure mediator = <span class="keyword">new</span> MediatorStructure();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//房主和租房者只需要知道中介机构即可</span></span><br><span class="line">        HouseOwner houseOwner = <span class="keyword">new</span> HouseOwner(<span class="string">"包租婆"</span>, mediator);</span><br><span class="line">        Tenant tenant = <span class="keyword">new</span> Tenant(<span class="string">"酱爆"</span>, mediator);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//中介结构要知道房主和租房者</span></span><br><span class="line">        mediator.setHouseOwner(houseOwner);</span><br><span class="line">        mediator.setTenant(tenant);</span><br><span class="line"></span><br><span class="line">        tenant.constact(<span class="string">"怎么停水啦？"</span>);</span><br><span class="line">        houseOwner.constact(<span class="string">"你没交水费"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">输出结果：</span><br><span class="line">房主:包租婆,获得信息：包租婆，怎么停水啦？</span><br><span class="line">租房者:酱爆,获得信息：打死你呀的！！</span><br></pre></td></tr></table></figure>



<p><strong>总结</strong></p>
<ul>
<li>简化了对象之间的关系，将系统的哥哥对现象之间相互关联进行封装，将各个同事解耦</li>
<li>使得多对多的关系变成了一对多的关系，简化对象之间的直接交互</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li>由于中介者对象封装了系统中对象之间的交互，导致器变得非常复杂，随着同事类的增加，维护难度会逐渐增大</li>
<li>其实中介者模式的缺点和优点在<code>MVC</code>模式体现的都很明显，所有的交互逻辑在<code>Controller</code>中，但是交互复杂之后经常导致<code>Controller</code>对象过于臃肿，难以维护更加不要说扩展了</li>
</ul>
<p><strong>JDK:</strong></p>
<ul>
<li>All scheduleXXX() methods of <a href="http://docs.oracle.com/javase/8/docs/api/java/util/Timer.html" target="_blank" rel="noopener">java.util.Timer</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-" target="_blank" rel="noopener">java.util.concurrent.Executor#execute()</a></li>
<li>submit() and invokeXXX() methods of <a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html" target="_blank" rel="noopener">java.util.concurrent.ExecutorService</a></li>
<li>scheduleXXX() methods of <a href="http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html" target="_blank" rel="noopener">java.util.concurrent.ScheduledExecutorService</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html#invoke-java.lang.Object-java.lang.Object...-" target="_blank" rel="noopener">java.lang.reflect.Method#invoke()</a></li>
</ul>
<h4 id="5-状态模式（State）"><a href="#5-状态模式（State）" class="headerlink" title="5. 状态模式（State）"></a>5. 状态模式（State）</h4><ul>
<li>定义：类的内部状态改变的时候，可以改变它的行为<ul>
<li>可能描述的有点模糊，举个栗子（真就是举个栗子）</li>
</ul>
</li>
</ul>
<p>（Android系统在未root时，是无法卸载系统预装应用的；root成功，开启root权限之后，不仅可以卸载系统预装应用，还可以使用xposed，简直就是可以为所欲为啊！这就可以看出，Android系统在root的状态变更时，影响到了自身的某些行为，进而某些行为也相对应的发生了改变。）</p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200703/083558850.webp" alt="mark"></p>
<ul>
<li><p>从UML类图上来看，结构似乎和策略模式是一模一样的。</p>
</li>
<li><p>状态模式的关键点在于不同的状态下，对于同一行为有不同的响应，避免使用<code>if-else</code>或<code>switch-case</code>区分状态所带来的代码臃肿；在保持代码结构的清晰的同时还保证了扩展和维护性。</p>
</li>
</ul>
<p><strong>代码实现</strong></p>
<ul>
<li>平常使用的登陆或者注销状态，经常使用的收藏和关注的功能，然而没有登录会提示去登陆，功能是无法使用的。</li>
<li>实现收藏功能很容易出现以下代码：</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">collect</span><span class="params">(Context context)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(isLogin())&#123;</span><br><span class="line">        <span class="comment">//收藏</span></span><br><span class="line">    &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">        <span class="comment">//去登陆</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>如果还需要添加关注功能的话，又需要再次判断是否登录，一旦需要判断登录的功能一多，这种coding方式就会成为噩梦。</strong></p>
<ul>
<li>接下来使用状态模式可以很好的避免这种情况，首先我们需要抽象出一个公共行为接口<code>UserState</code> ，其中包含<code>collect()</code>以及<code>follow()</code>方法。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserState</span> </span>&#123;</span><br><span class="line">    <span class="comment">/** 收藏行为 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">collect</span><span class="params">(Context context)</span></span>;</span><br><span class="line">    <span class="comment">/** 关注行为 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">follow</span><span class="params">(Context context)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>对应<strong>登录</strong>和<strong>未登录</strong>两种状态会有两种不同的实现方式，如下</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LoginState</span> <span class="keyword">implements</span> <span class="title">UserState</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">collect</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"收藏成功"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">follow</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"关注成功"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LogoutState</span> <span class="keyword">implements</span> <span class="title">UserState</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">collect</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"请登录"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">follow</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"请登录"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>接下来，我们需要一个类来关联登录状态与外界，并为外界提供<code>collect()</code>函数和<code>follow()</code>函数对应的调用；并将<code>LoginState</code>和<code>LogoutState</code>结合起来。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里主要是实现状态模式，</span></span><br><span class="line"><span class="comment">// 对于用户这个案例来说，结合单例模式可能会更好。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserContext</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserState LOGIN = <span class="keyword">new</span> LoginState();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> UserState LOGOUT = <span class="keyword">new</span> LogoutState();</span><br><span class="line">    <span class="keyword">private</span> UserState mState = LOGOUT;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 登录，供外部调用 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">login</span><span class="params">()</span></span>&#123;</span><br><span class="line">        mState = LOGIN;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 退出登录，供外部调用 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">logout</span><span class="params">()</span></span>&#123;</span><br><span class="line">        mState = LOGOUT;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 调用mState的coolect() */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">collect</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">        mState.collect(context);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 调用mState的follow() */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">follow</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">        mState.follow(context);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 此方法判断不太严谨，请忽略 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isLogin</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mState <span class="keyword">instanceof</span> LoginState;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意：</p>
<ul>
<li><p>这里的<code>UserContext</code> 提供给外部的<code>collect()</code> 和 <code>follow()</code> 方法的同时，还要提供<code>login()</code> 和 <code>logout（）</code>两个方法给外部调用来更改<code>UserContext</code> 的内部状态。</p>
</li>
<li><p>这里主要是实现状态模式，<strong>对于用户这个案例来说，通常会结合单例模式使用的更好</strong></p>
</li>
</ul>
<blockquote>
<p>从某种程度上来说，提供login()和logout()两个方法是为了防止外界篡改内部的行为，因为外部无法传入自己实现的UserState对象，也就无法修改UserContext对应状态的行为。</p>
<p>确实也看到有些文章，在设置状态时，状态是由外部传入的，这种写法觉得不太合适，所以这里单独说明。</p>
</blockquote>
<p><strong>扩展思考</strong></p>
<p>其实对于实际情况来说，用户的操作行为可能远远不止这两个方法，若还有需要状态判断登录状态的行为，我们可以继续在<code>UserState</code>接口中为新增的行为添加相应的方法；但是，我们也可以预见，<code>UserState</code>接口将会越来越庞大。</p>
<p>这种问题的话，也可以将每个行为独立成一个接口，进而让<code>UserState</code>继承各个行为接口，或者将<code>UserState</code>改成抽象类实现各个行为接口。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//独立的收藏的接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ICollect</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">collect</span><span class="params">(Context context)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//独立的关注的接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">IFollow</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">follow</span><span class="params">(Context context)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//所有行为接口的集合</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserState</span> <span class="keyword">extends</span> <span class="title">ICollect</span>,<span class="title">IFollow</span></span>&#123;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><strong>总结：</strong><ul>
<li>状态模式的关键点在于不同的状态下，对于同一行为有不同的响应，避免使用<code>if-else</code>或<code>switch-case</code>区分状态所带来的代码臃肿；在保持代码结构的清晰的同时还保证了扩展和维护性。</li>
<li>随着状态的增加，系统类和对象的个数也会增加，使用不当会导致程序结构变得越来越庞大。</li>
</ul>
</li>
</ul>
<h4 id="6-备忘录模式（Memento）"><a href="#6-备忘录模式（Memento）" class="headerlink" title="6. 备忘录模式（Memento）"></a>6. 备忘录模式（Memento）</h4><ul>
<li>定义：在不破坏封闭的前提下，将对象的当前内部状态保存到对象之外，之后可以再次恢复到此对象。（典型的例子就是游戏存档和读档的这个行为）</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200703/090135433.webp" alt="mark"></p>
<ul>
<li><strong>Originator:</strong> 发起者，负责创建一个备忘录，并且可以记录，恢复自身的内部状态。可以根据需要决定<code>Memeto</code>保存自身的那些内部状态。</li>
<li><strong>Memento:</strong>备忘录，用于存储<code>Originator</code>的状态，防止<code>Originator</code>以外的对象访问<code>Memento</code></li>
<li><strong>Caretaker</strong> : 备忘录管理者，负责存储备忘录，不能对备忘录的内容进行操作和访问，只能将备忘录传递给其他对象。</li>
</ul>
<p><strong>代码实现：</strong></p>
<ul>
<li>发起者通过创建一个新的备忘录对象来保存自己的内部状态</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//看出其模式的封闭性，对于状态得存储只有Originator知道</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Originator</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String state;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建一个新的备忘录对象</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Memento <span class="title">createMemento</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Memento(state);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将发起者的状态恢复到备忘录的状态</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">restore</span><span class="params">(Memento memento)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.state = memento.getState();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>备忘录，将发起人对象传入的状态存储起来。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Memento</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String state;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Memento</span><span class="params">(String state)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.state = state;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getState</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> state;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setState</span><span class="params">(String state)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.state = state;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>备忘录管理者，负责保存备忘录对象，但是从不修改（甚至不查看）备忘录对象的内容。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Caretaker</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Memento mMemento;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Memento <span class="title">restoreMemento</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mMemento;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">storeMemengto</span><span class="params">(Memento memento)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.mMemento = memento;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>基本上这就是备忘录模式的结构了，可以看出其模式的封闭性，对于状态的存储只有<code>Originator</code> 知道。</p>
<p><strong>总结：</strong></p>
<ul>
<li>备忘录模式在不破坏封装性的条件下，通过备忘录对象存储另外一个对象的内部状态的快照，在将来合适的时候把这个对象还原到存储的状态。</li>
</ul>
<p><strong>优点：</strong></p>
<ul>
<li>给用户提供了一种可以恢复状态的机制，可以是用户能够方便的回到某个历史的状态</li>
<li>实现了信息的封装，是的用户不需要关心状态的保存细节</li>
</ul>
<p><strong>缺点</strong></p>
<ul>
<li><strong>现在很多序列化的技术已经比备忘录模式优秀很多</strong></li>
</ul>
<p><strong>JDK：</strong></p>
<ul>
<li>java.io.Serializable</li>
</ul>
<h4 id="7-迭代器模式（Iterator）"><a href="#7-迭代器模式（Iterator）" class="headerlink" title="7. 迭代器模式（Iterator）"></a>7. 迭代器模式（Iterator）</h4><ul>
<li><p>迭代器模式：行为型设计模式之一。</p>
</li>
<li><p>定义：提供一种方法顺序访问要给容器对象中的各个元素，而有不需要暴露该对象的内部数据存储的实现。<strong>（应用于数据结构）</strong></p>
</li>
<li><p>迭代器模式源于对数据集合的访问，1. 将遍历的方法封装到数据集合中，2. 或者不提供方法由用户自己遍历。</p>
<ul>
<li>以上两种方式都有弊端</li>
<li>封装在数据集合中，那数据集合似乎多了些功能，数据集合不仅要维护自身数据元素，还要对外提供遍历的结构方法，遍历状态下还不能对同一个数据集合进行多个遍历操作。</li>
<li>不提取公共的遍历方法，如果让使用者自己实现，又会让容器内部细节对外暴露。</li>
</ul>
</li>
</ul>
<p><strong>UML类图：</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200705/090918549.webp" alt="mark"></p>
<ul>
<li>Iterator：迭代器接口，负责定义，访问和遍历元素的接口</li>
<li>ConcreteIterator：具体的迭代器，实现迭代器接口，并记录遍历的当前位置。</li>
<li>Aggregate: 容器接口，负责创建具体的迭代器角色的接口。</li>
<li>ConcreteAggregate：具体的容器类，具体迭代器和该容器相关联。</li>
</ul>
<p><strong>代码实现</strong></p>
<ol>
<li><strong>迭代器接口</strong></li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Iterator</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回当前位置元素并将位置移至下一位</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 是否还有下一个元素</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> true-有，false-没有</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol start="2">
<li><strong>具体的迭代器</strong></li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteIterator</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> List&lt;T&gt; list;</span><br><span class="line">    <span class="comment">// 用于移动的游标</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> cursor = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ConcreteIterator</span><span class="params">(List&lt;T&gt; list)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.list = list;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        T t = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (hasNext())&#123;</span><br><span class="line">            t = list.get(cursor++);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> t;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> cursor != list.size();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="3">
<li><strong>容器接口</strong></li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Aggregate</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">// 添加一个元素</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(T obj)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 删除一个元素</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">(T obj)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取迭代器对象</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Iterator&lt;T&gt; <span class="title">iterator</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="4">
<li><strong>具体的容器类</strong></li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteAggregate</span>&lt;<span class="title">T</span>&gt; <span class="keyword">implements</span> <span class="title">Aggregate</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;T&gt; list = <span class="keyword">new</span> ArrayList&lt;T&gt;();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(T obj)</span> </span>&#123;</span><br><span class="line">        list.add(obj);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">(T obj)</span> </span>&#123;</span><br><span class="line">        list.remove(obj);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Iterator&lt;T&gt; <span class="title">iterator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ConcreteIterator(list);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>优点</strong></p>
<ul>
<li>封装性好，对外部的使用者来说可以以相同的API来获取不同结构的数据集合中的数据元素，不需要关心遍历算法，简化了遍历方式</li>
<li>便于扩展遍历方式，例如：可以创建一个正序迭代器同时还有一个倒序迭代器</li>
</ul>
<p>看了<code>ArratList</code>代码中的<code>Iterator</code>主要的代码和上述是基本一致的，添加了更多的判断。而对于不同的数据结构<code>Map</code>、<code>Set</code>根据其不同的实现方式<code>Iterator</code>的实现方式也各不相同。</p>
<p>迭代器模式，在数据结构中有广泛的使用，由于现在很多语言的API都提供了高效数据集合API，所以我们很少有需要去运用此模式，有兴趣的可以学习数据结构或者阅读源码来深入了解迭代器模式的实现。</p>
<p><strong>JDK</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html" target="_blank" rel="noopener">java.util.Iterator</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/Enumeration.html" target="_blank" rel="noopener">java.util.Enumeration</a></li>
</ul>
<h4 id="8-职责链模式（Chain-of-Responsibility）"><a href="#8-职责链模式（Chain-of-Responsibility）" class="headerlink" title="8. 职责链模式（Chain of Responsibility）"></a>8. 职责链模式（<strong>Chain of Responsibility</strong>）</h4><ul>
<li><p><strong>责任链模式（Chain of Responsibility）</strong>，行为型设计模式之一。</p>
</li>
<li><p>什么是责任链呢？这个链很像<strong>数据结构</strong>中的<strong>单链表</strong>，链中的每个节点都有自己的职责，同时也持有下一个节点的引用。<strong>属于自己职责范围内的请求就自己处理，并完成请求的处理；而不属于的职责就传递给下一个节点。每个节点都是如此循环，直至请求被处理或者已经没有处理节点。</strong></p>
</li>
<li><p>这种设计模式为了避免<strong>请求的发送者和接收者的耦合关系</strong>，而责任链就是中间请求处理者（其中可能包括多个有可能处理请求的对象，并将这些对象连成一条链。这样也使得请求发送者无需关注处理细节和请求的传递。）</p>
</li>
</ul>
<p><strong>UML类图</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200705/111235950.webp" alt="mark"></p>
<ul>
<li>Client：请求发送者</li>
<li>Handler：抽象处理者，声明一个请求方法，并且保持一个对下一个处理节点Hanler的引用</li>
<li>ConcreteHandler： 具体的处理角色。对请求进行处理，如果不能处理就转发给下一个对象处理。</li>
</ul>
<blockquote>
<p>这是一个基本的结构描述，实际应用中会有进一步的封装。</p>
</blockquote>
<p><strong>代码实现：</strong></p>
<ul>
<li>以公司正常请假为例，1天以内的假需要客户端部门主管签字，3天以内（不包含3天）需要技术部门主管签字，3天及以上就需要找CEO签字。</li>
<li>最简单的就是使用<code>if-else</code>实现，但是结构并不是那么美观，试着用责任链模式来实现。</li>
<li>首先是假条的类，包含姓名、请假原因和请假时间,使用<code>final</code> 声明是为了便面外部修改属性而已。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 假条的对象 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LeaveNote</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 请假人</span></span><br><span class="line">    <span class="keyword">final</span> String name;</span><br><span class="line">    <span class="comment">// 请假理由</span></span><br><span class="line">    <span class="keyword">final</span> String reason;</span><br><span class="line">    <span class="comment">// 请假天数</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">int</span> leaveDays;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LeaveNote</span><span class="params">(String name, String reason, <span class="keyword">int</span> leaveDays)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.reason = reason;</span><br><span class="line">        <span class="keyword">this</span>.leaveDays = leaveDays;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getReason</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> reason;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getLeaveDays</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> leaveDays;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>接下来就是比较重要的<code>Handler</code>类，代码比较简单，下一个<code>Handler</code>的对象引用，再就是<code>handle()</code>和<code>setNextHandler()</code>方法，不多解释了，和之前的UML图中的的结构是一样的</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Handler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 下一个对象的引用</span></span><br><span class="line">    <span class="keyword">public</span> Handler nextHandler;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 处理什么东西</span></span><br><span class="line">    <span class="function"><span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">hand</span><span class="params">(LeaveNote note)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置下一个对象的引用</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setNextHandler</span><span class="params">(Handler nextHandler)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.nextHandler = nextHandler;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>现在我们需要实现客户端主管、技术主管和CEO三个<code>Handler</code>的子类</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClientLeader</span> <span class="keyword">extends</span> <span class="title">Handler</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">hand</span><span class="params">(LeaveNote note)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (note.leaveDays &lt; <span class="number">3</span>)&#123;</span><br><span class="line">            System.out.println(<span class="string">"部门主管同意"</span> + note.name + <span class="string">"请假"</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            nextHandler.hand(note);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TechnologyLeader</span> <span class="keyword">extends</span> <span class="title">Handler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">hand</span><span class="params">(LeaveNote note)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (note.leaveDays &lt; <span class="number">3</span>)&#123;</span><br><span class="line">            System.out.println(<span class="string">"技术主管"</span> + note.name + <span class="string">"请假"</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            nextHandler.hand(note);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CEO</span> <span class="keyword">extends</span> <span class="title">Handler</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">hand</span><span class="params">(LeaveNote note)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"CEO同意"</span> + note.name + <span class="string">"请假"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>接着我们就需要测试调用我们写好的代码了</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** 测试方法 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">()</span></span>&#123;</span><br><span class="line">        LeaveNote note = <span class="keyword">new</span> LeaveNote(<span class="string">"朱酱酱"</span>, <span class="string">"回家过年"</span>, <span class="number">3</span>);</span><br><span class="line">        requestLevel(note);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">/** 具体的封装方法 */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">requestLevel</span><span class="params">(LeaveNote note)</span> </span>&#123;</span><br><span class="line">        ClientLeader leader = <span class="keyword">new</span> ClientLeader();</span><br><span class="line">        TechnologyLeader technologyLeader = <span class="keyword">new</span> TechnologyLeader();</span><br><span class="line">        CEO ceo = <span class="keyword">new</span> CEO();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 链表的形式</span></span><br><span class="line">        leader.setNextHandler(technologyLeader);</span><br><span class="line">        technologyLeader.setNextHandler(ceo);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 从头节点开始判断是否能够处理</span></span><br><span class="line">        leader.hand(note);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Client client = <span class="keyword">new</span> Client();</span><br><span class="line">        client.test();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里完成了，比较简单地责任链模式就完成了。</p>
<p>其实对于链表，nextHandler还可以作为构造函数的参数传入，于是代码变成这个样子</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">requestLeave</span><span class="params">(LeaveNote leaveNote)</span></span>&#123;</span><br><span class="line">    CEO ceo = <span class="keyword">new</span> CEO(<span class="keyword">null</span>);</span><br><span class="line">    TechnologyLeader technologyLeader = <span class="keyword">new</span> TechnologyLeader(ceo);</span><br><span class="line">    ClientLeader clientLeader = <span class="keyword">new</span> ClientLeader(technologyLeader);</span><br><span class="line"></span><br><span class="line">    clientLeader.hand(leaveNote);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>总结：</strong></p>
<p><strong>责任链的优点就在于请求者和接受者松散耦合，以及能动态组合职责。</strong></p>
<p>（例子中可以看出，请求者不知道接收者是谁，也不知道具体的处理过程，只需要发出请求就可以了。而对于每一个职责对象来说，也不关心请求者和其他职责对象，虽然由下一个职责对象的引用，但是还是只负责处理自己的职责部分就好了，其他部分交给其他职责对象去处理）</p>
<p>动态组合职责则是利用职责的拆分，可以灵活的组合形式责任链，从而可以灵活的分配职责对象，也可以灵活的实现职责对象。</p>
<p><strong>PS：其实刚开始学习责任链模式的时候，我在想：“这种设计模式并没有做到很好地解耦啊！每个Handler还需要持有下一个Handler对象的引用，这不是造成更高的耦合度了么！”。之后才看明白，责任链的解决的时发出请求的一方和接受请求的一方的耦合度的问题，而处理这一切的<code>Handler</code>就是解决方案，所以<code>Handler</code>之间的耦合基本算是内部的</strong></p>
<p><strong>JDK:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29" target="_blank" rel="noopener">java.util.logging.Logger#log()</a></li>
<li><a href="https://commons.apache.org/proper/commons-chain/index.html" target="_blank" rel="noopener">Apache Commons Chain</a></li>
<li><a href="http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-" target="_blank" rel="noopener">javax.servlet.Filter#doFilter()</a></li>
</ul>
<h4 id="9-命令模式（Command-）"><a href="#9-命令模式（Command-）" class="headerlink" title="9. 命令模式（Command ）"></a>9. 命令模式（<strong>Command</strong> ）</h4><p>定义：将命令封装成对象，具有以下作用</p>
<ul>
<li>使用命令来参数化其他对象</li>
<li>将命令放入队列进行排序</li>
<li>将命令的操作记录到日志中</li>
<li>支持可撤销的操作</li>
</ul>
<p><strong>UML类图：</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200705/115952847.webp" alt="mark"></p>
<ul>
<li>Client：客户端，也就是发出命令者。</li>
<li>Invoker:执行者，该类的职业就是调用命令对象执行具体的请求操作，相关的方法我们称为行动方法。</li>
<li>Command:命令，定义所有具体命令类的接口。</li>
<li>ConcreteCommand：具体命令，实现了<code>Command</code>接口，在<code>execute()</code>方法中调用<code>Receiver</code>相关的方法。</li>
<li>Receiver：接收者，负责执行具体的逻辑。</li>
</ul>
<blockquote>
<p>整体来看，命令模式比较繁琐，执行一个指令的过程被分解成了好几部分，相对的复杂度也提升了；但是命令模式结构还是很清晰的。</p>
</blockquote>
<p><strong>代码实现：</strong></p>
<p>​    记得有个段子：</p>
<p>如果有一个按钮，按下以后会忘记一切事情，你会怎样？<br>咦，这里有个按钮，按一下<br>咦，这里有个按钮，按一下<br>咦，这里有个按钮，按一下</p>
<p>。。。</p>
<p>其实这个按下这个按钮就是执行了一个命令，是一个让你忘记一切的命令（于是自己开始递归，最后还Stack Overflow了？）</p>
<ul>
<li>首先我们需要一个接收者<code>Receiver</code> ， 来实现我们所传递的命令</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里接收者只执行这一个命令，</span></span><br><span class="line"><span class="comment">// 复杂的情况们可以继承Receiver来实现不同的接收者处理不同的命令。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Receiver</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">action</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="comment">//具体命令操作</span></span><br><span class="line">        System.out.println(<span class="string">"清除所有记忆"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>这里接收者只执行这一个命令，复杂的情况们可以继承<code>Receiver</code>来实现不同的接收者处理不同的命令。</p>
</blockquote>
<ul>
<li>再是一个命令的接口<code>Command</code>和一个具体的<code>Command</code>实现类<code>CleanMemoryCommand</code>，很简单，只有一个没有实现的<code>execute()</code>方法</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 命令接口 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Command</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 清除记忆命令 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CleanMemoryCommand</span> <span class="keyword">implements</span> <span class="title">Command</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    Receiver receiver;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">CleanMemoryCommand</span><span class="params">(Receiver receiver)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.receiver = receiver;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        receiver.action();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>这样，命令和接收者都有了，我们只差一个供用户直接使用的<code>Invoker</code>了</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Invoker</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Command command;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Invoker</span><span class="params">(Command command)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.command = command;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">actoin</span><span class="params">()</span></span>&#123;</span><br><span class="line">        command.execute();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>调用的代码就比较简单了，这代码可以封装成一个方法</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span>&#123;</span><br><span class="line">        Receiver receiver = <span class="keyword">new</span> Receiver();</span><br><span class="line">        CleanMemoryCommand command = <span class="keyword">new</span> CleanMemoryCommand(receiver);</span><br><span class="line">        Invoker invoker = <span class="keyword">new</span> Invoker(command);</span><br><span class="line">        invoker.actoin();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>总结：</strong></p>
<ul>
<li>命令模式看似简单，细想来其实存在着几乎所有设计模式的通病，那就是大量衍生类的创建，这是一个不可避免的问题。</li>
<li>尽管如此，也给我们带来了许多好处，更弱的耦合性、更灵活的控制性以及更好的扩展性。但是最后还是那句话，用不用，还是根据实际情况而定。</li>
</ul>
<h4 id="10-访问者模式（Visitor）"><a href="#10-访问者模式（Visitor）" class="headerlink" title="10 .访问者模式（Visitor）"></a>10 .访问者模式（Visitor）</h4><ul>
<li><p>访问者模式是一种将数据操作和数据结构分离的设计模式，是23种设计模式中非常复杂的一种设计模式。</p>
</li>
<li><p><strong>定义：封装一些作用与某种数据结构中的各元素的操作（访问），可以在不改变这个数据的前提下定义作用于这些元素的新操作。</strong></p>
</li>
<li><p>顾名思义：某些不能改变的数据，对于不同的访问者有不同的操作（或者访问），为不同的访问者提供相对应的操作。例如：公司CEO就能看到公司所有的真实财报数据，而作为一个员工可能就只能知道同比去年的增长比例。</p>
</li>
</ul>
<p><strong>UML类图：</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200711/090626735.webp" alt="mark"></p>
<ul>
<li><p><strong>Visitor</strong>：<strong>访问者抽象类</strong>（或者接口），它定义了对每一个元素（Element）访问的行为，它的参数就是可以访问的元素；理论上，它的方法个数和元素个数是一样的。因此，<strong>访问者模式要求元素的类族要稳定，不能频繁的添加，移除元素。如果出现频繁修改Visitor接口的情况，说明可能并不适合使用访问者模式。</strong></p>
</li>
<li><p><strong>ConcreteVisitor</strong> ： 具体的访问者，需要实现每一个元素类访问时所产生的具体的行为 。</p>
</li>
<li><p><strong>Element</strong>： 元素接口（或者抽象类），它定义了一个接收访问者的方法（<code>accept</code>） 方法，意义在于对于每一个元素都要刻意被访问者访问。</p>
</li>
<li><p><strong>ElementA、ElementB：</strong> 具体的元素类，提供接收访问方法的具体实现。而这个具体实现，通常情况下是使用访问者提供的访问该元素类的方法。</p>
</li>
<li><p><strong>ObjectStructure：</strong> 定义当中所提到的对象结构，<strong>对象结构是一个抽象表述</strong>，它内部管理了元素集合，并且可以迭代这些元素供访问者访问。</p>
</li>
</ul>
<blockquote>
<p>PS：访问者模式违反了迪米特原则（对访问者公布元素细节）以及依赖倒置原则（依赖了具体类，没有依赖抽象），由此可见，此模式需要应用在特定的情况中。</p>
</blockquote>
<p><strong>案例实现</strong></p>
<p>这里就以公司为例，公司员工暂且分为开发人员和运营人员，而公司的CEO和CTO对于不同员工的KPI关注点不同，因此我们需要做出不同的处理，接着看看代码实现</p>
<ul>
<li><strong>员工基类</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Element接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Staff</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">int</span> kpi;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Staff</span><span class="params">(String name, <span class="keyword">int</span> kpi)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">        <span class="keyword">this</span>.kpi = kpi;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 接受visitor的访问</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">(Visitor visitor)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>具体员工类</strong></li>
</ul>
<p>具体的员工，根据各自不同的职责添加了不同的方法，开发人员的KPI和代码产量相关，于是添加了获取代码行数的方法，而运营人员的KPI和新增用户量相关，于是添加了获取新增用户数的方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 开发人员</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Developer</span> <span class="keyword">extends</span> <span class="title">Staff</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Developer</span><span class="params">(String name, <span class="keyword">int</span> kpi)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name, kpi);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">(Visitor visitor)</span> </span>&#123;</span><br><span class="line">        visitor.visit(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 代码量</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getCodeLines</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Random().nextInt(<span class="number">10</span> * <span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//运维人员</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Operator</span> <span class="keyword">extends</span> <span class="title">Staff</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Operator</span><span class="params">(String name, <span class="keyword">int</span> kpi)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name, kpi);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">(Visitor visitor)</span> </span>&#123;</span><br><span class="line">        visitor.visit(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 代码量</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getNewUserNum</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Random().nextInt(<span class="number">10</span> * <span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><strong>访问者</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 访问者Visitor</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Visitor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 访问开发人员</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">(Developer developer)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 访问运营人员</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">(Operator operator)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里可以看到，直接从方法上就区分<code>Developer</code>和<code>Operator</code>，这里主要考虑到的是，如果使用基类<code>Staff</code>作为参数的话代码就会是这个样子</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">(Staff staff)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(staff <span class="keyword">instanceof</span> Developer)&#123;</span><br><span class="line">            Developer developer = (Developer)staff;</span><br><span class="line">            System.out.print(<span class="string">"开发"</span> + developer.name</span><br><span class="line">                    + <span class="string">",KPI:"</span> + developer.kpi + <span class="string">"，代码"</span> + developer.getCodeLines() + <span class="string">"行"</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span> <span class="keyword">if</span>(staff <span class="keyword">instanceof</span> Operator)&#123;</span><br><span class="line">            Operator operator = (Operator) staff;</span><br><span class="line">            System.out.print(<span class="string">"运营"</span> + operator.name + <span class="string">",KPI:"</span> + operator.kpi);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>可以看到，在<code>visit()</code> 方法中，我们就需要判断参数类型和类型转换，这样代码就难以扩展和维护。</p>
<blockquote>
<p>这是访问者模式的一个优点，也是一个缺点，优点在于代码清晰，某种程度上代码的维护和扩张更好；而缺点也是一样，如果需要添加一类<code>Staff</code>，所有的<code>Visitor</code>都需要在实现一个新的<code>visit()</code>方法。</p>
</blockquote>
<ul>
<li><strong>具体的访问者</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// CTO对于不同人员操作不同</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CTOVisitor</span> <span class="keyword">implements</span> <span class="title">Visitor</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">(Developer developer)</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">"开发"</span> + developer.name</span><br><span class="line">                + <span class="string">",KPI:"</span> + developer.kpi + <span class="string">"，代码"</span> + developer.getCodeLines() + <span class="string">"行"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">(Operator operator)</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">"运营"</span> + operator.name + <span class="string">",KPI:"</span> + operator.kpi);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// CEO对于不同人员操作不同</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CEOVisitor</span> <span class="keyword">implements</span> <span class="title">Visitor</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">(Developer developer)</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">"开发"</span> + developer.name + <span class="string">",KPI:"</span> + developer.kpi);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">(Operator operator)</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">"运营"</span> + operator.name</span><br><span class="line">                + <span class="string">",KPI:"</span> + operator.kpi + <span class="string">"新增用户："</span> + operator.getNewUserNum());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><strong>对象结构</strong></li>
</ul>
<p>这里的对象结构，直接就设定成了公司，集合就是员工们</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对象结构</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Company</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> List&lt;Staff&gt; staffList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">action</span><span class="params">(Visitor visitor)</span></span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (Staff staff : staffList) &#123;</span><br><span class="line">            staff.accept(visitor);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 新增</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addStaff</span><span class="params">(Staff staff)</span></span>&#123;</span><br><span class="line">        staffList.add(staff);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><strong>客户端代码：</strong></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Company company = <span class="keyword">new</span> Company();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        company.addStaff(<span class="keyword">new</span> Developer(<span class="string">"Bruce Wayne"</span>,<span class="number">10</span>));</span><br><span class="line">        company.addStaff(<span class="keyword">new</span> Developer(<span class="string">"ClarkKent"</span>,<span class="number">20</span>));</span><br><span class="line">        company.addStaff(<span class="keyword">new</span> Developer(<span class="string">"Barry Allen"</span>,<span class="number">30</span>));</span><br><span class="line"></span><br><span class="line">        company.addStaff(<span class="keyword">new</span> Operator(<span class="string">"Diana Prince"</span>,<span class="number">40</span>));</span><br><span class="line">        company.addStaff(<span class="keyword">new</span> Operator(<span class="string">"Oliver Queen"</span>,<span class="number">50</span>));</span><br><span class="line">        company.addStaff(<span class="keyword">new</span> Operator(<span class="string">"Dinah Lance"</span>,<span class="number">60</span>));</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        CEOVisitor ceo = <span class="keyword">new</span> CEOVisitor();</span><br><span class="line">        company.action(ceo);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        CTOVisitor cto = <span class="keyword">new</span> CTOVisitor();</span><br><span class="line">        company.action(cto);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>具体输出如下：</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">CEO所看到的======</span><br><span class="line">开发Bruce Wayne,KPI:<span class="number">6</span></span><br><span class="line">开发ClarkKent,KPI:<span class="number">2</span></span><br><span class="line">开发Barry Allen,KPI:<span class="number">8</span></span><br><span class="line">运营Diana Prince,KPI:<span class="number">4</span>,新增用户：<span class="number">46642</span></span><br><span class="line">运营Oliver Queen,KPI:<span class="number">1</span>,新增用户：<span class="number">7687</span></span><br><span class="line">运营Dinah Lance,KPI:<span class="number">3</span>,新增用户：<span class="number">67382</span></span><br><span class="line"> </span><br><span class="line">CTO所看到的======</span><br><span class="line">开发Bruce Wayne,KPI:<span class="number">6</span>,代码<span class="number">8285</span>行</span><br><span class="line">开发ClarkKent,KPI:<span class="number">2</span>,代码<span class="number">8351</span>行</span><br><span class="line">开发Barry Allen,KPI:<span class="number">8</span>,代码<span class="number">658</span>行</span><br><span class="line">运营Diana Prince,KPI:<span class="number">4</span></span><br><span class="line">运营Oliver Queen,KPI:<span class="number">1</span></span><br><span class="line">运营Dinah Lance,KPI:<span class="number">3</span></span><br></pre></td></tr></table></figure>



<h5 id="10-1-分派"><a href="#10-1-分派" class="headerlink" title="10.1 分派"></a>10.1 分派</h5><ul>
<li>变量名<strong>被声明时的类型叫做变量的静态类型</strong>(Static Type)，静态变量类型又可以叫做明显类型(Apparent Type)；而变量所引起的对象的正式类型叫做变量的<strong>实际类型</strong>(Actual Type)。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// List静态类型，Arraylist动态类型</span></span><br><span class="line">List list = <span class="keyword">new</span> ArrayList();</span><br></pre></td></tr></table></figure>

<ul>
<li>在java代码中有一种很常见的写法，声明父类对象创建子类对象（比如上面 声明是 <code>List</code> 类型（也就是静态类型即明显类型）），创建的是<code>ArrayList</code> 的对象（实际类型）。</li>
<li><strong>这里需要提到一个词，分派（Dispatch</strong>） 。 当使用上述形式声明并创建对象，根据对象的类型对方法进行选择。这就是分派，而分配又可以分为<strong>静态分派（Static Dispatch）</strong> 和 <strong>动态分派（Dynamic Dispatch）</strong><ul>
<li><strong>静态分派：对应的就是编译时，根据静态类型信息发生的分派。方法重载就属于静态分派</strong></li>
<li><strong>动态分派：对应的就是运行时，动态的自动的换掉某个方法。方法的重写就属于动态分派。</strong></li>
</ul>
</li>
</ul>
<p><strong>静态分派</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方法的重载</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Staff</span> </span>&#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Developer</span> <span class="keyword">extends</span> <span class="title">Staff</span></span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Operator</span> <span class="keyword">extends</span> <span class="title">Staff</span></span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行类，<code>execute()</code>方法有三个<strong>重载方法</strong>，方法的参数分别上面对应的三个类型<code>Staff</code>、<code>Developer</code>、<code>Operator</code>的对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Execute</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Staff staff)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"员工"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Developer developer)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"开发人员"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Operator operator)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"运营人员"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>测试代码和结果</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] agrs)</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"运行结果："</span>);</span><br><span class="line">       </span><br><span class="line">        Staff staff = <span class="keyword">new</span> Staff();</span><br><span class="line">        Staff staff1 = <span class="keyword">new</span> Developer();</span><br><span class="line">        Staff staff2 = <span class="keyword">new</span> Operator();</span><br><span class="line">        </span><br><span class="line">        Execute execute = <span class="keyword">new</span> Execute();</span><br><span class="line">        execute.execute(staff);</span><br><span class="line">        execute.execute(staff1);</span><br><span class="line">        execute.execute(staff2);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">运行结果：</span><br><span class="line">员工</span><br><span class="line">员工</span><br><span class="line">员工</span><br></pre></td></tr></table></figure>



<p><strong>静态分派总结：</strong></p>
<ul>
<li>可以推断出，传入三个对象，最后执行的方法都是参数类型是<code>Staff</code> 的方法，即使三个对象有不同的真实类型。</li>
<li><strong>方法重载中实际起作用的是他们的静态类型，也就是在编译期间就完成了分派，即静态分派</strong></li>
</ul>
<p><strong>动态分派</strong></p>
<p>三个类自带<code>execute()</code>方法，<code>Developer</code>和<code>Operator</code>继承<code>Staff</code>，并重写了<code>execute()</code>方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方法的重写</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Staff</span> </span>&#123;</span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">()</span></span>&#123;</span><br><span class="line">       System.out.println(<span class="string">"员工"</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Developer</span> <span class="keyword">extends</span> <span class="title">Staff</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"开发人员"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Operator</span> <span class="keyword">extends</span> <span class="title">Staff</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"运营人员"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>测试代码以及结果</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] agrs)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"运行结果："</span>);</span><br><span class="line">        Staff staff = <span class="keyword">new</span> Staff();</span><br><span class="line">        staff.execute();</span><br><span class="line">        </span><br><span class="line">        Staff staff1 = <span class="keyword">new</span> Developer();</span><br><span class="line">        staff1.execute();</span><br><span class="line">        </span><br><span class="line">        Staff staff2 = <span class="keyword">new</span> Operator();</span><br><span class="line">        staff2.execute();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">运行结果：</span><br><span class="line">员工</span><br><span class="line">开发人员</span><br><span class="line">运营人员</span><br></pre></td></tr></table></figure>



<p><strong>动态分派总结</strong></p>
<ul>
<li>测试的情况相同，三个对象，其静态方法都是<code>Staff</code> ，而实际类型分别是<code>Staff</code> , <code>Developer</code> 和 <code>Operator</code> 。可以看到重写<code>execute()</code> 方法都生效了，各自输出了对应的内容</li>
<li><strong>java编译器在编译期间不总是知道哪些代码会被执行，因为编译器仅仅知道对象的静态类型，而不知道对象的真实类型；而方法的调用则是根据对象的真实类型，而不是静态类型。</strong></li>
</ul>
<h5 id="10-2-单分派和多分派"><a href="#10-2-单分派和多分派" class="headerlink" title="10.2 单分派和多分派"></a>10.2 单分派和多分派</h5><ul>
<li>首先要了解一个<strong>宗量</strong>的概念。一个方法所属的对象叫做方法的接收者，方法的接收者和方法的参量统称为方法的宗量。而根据分派可以基于多少种宗量，可以将面向对l象分为<strong>单分派语言和多分派语言。</strong><ul>
<li><strong>单分派语言</strong>根据一个宗量的类型（真实类型）进行对方法的选择</li>
<li><strong>多分派语言</strong>根据多个宗量的类型对方法进行选择。</li>
</ul>
</li>
<li><strong>那Java属于什么类型呢？</strong><ul>
<li><code>Java</code> 中<strong>静态分派时</strong>决定方法的选择的宗量包括方法的接收者和方法参数的静态类型。所以是<strong>多分派。</strong></li>
<li>在<strong>动态分派</strong>时，方法的选择只会考虑方法的接收者和实际类型，所以是<strong>单分派</strong></li>
<li><strong>其实<code>Java</code>语言是支持静态多分派和动态单分派的语言。</strong></li>
</ul>
</li>
</ul>
<h5 id="10-3-双重分派"><a href="#10-3-双重分派" class="headerlink" title="10.3 双重分派"></a>10.3 双重分派</h5><p>那双重分派又是什么呢？分派和访问者模式又有什么关系呢？接下来就会解释这些问题</p>
<p><code>java</code> 支持静态多分派和动态但分派，并不支持动态多分派，于是就有了两次单分派组成的双重分派来代替动态多分派。而访问者模式恰好使用到了双重分派的技术。</p>
<p><strong>双重分派</strong>技术就是在选择一个方法的时候，不仅仅要根据方法的接收者的运行时区别，还要根据运行时的参数进行区别（达到二次分派的效果）</p>
<p>在访问者模式中，客户端将具体的对象传递给访问者，也就是<code>staff.accpet(vistior)</code> 方法的调用，完成第一次分派；然后具体的访问者作为参数传入到具体的对象方法中，也就是<code>visitor.visit(this)</code>,将<code>this</code> 作为参数传递进去完成第二次分派。</p>
<p>双重分派也就是意味着得到的执行操作决定请求的种类和接收者的类型。双重分派的核心就是<code>this</code> 对象。</p>
<blockquote>
<p>从访问者模式可以看出，双重分派就是在方法的委派前面加上了继承的重写，使得从某种角度上来说重载变成了动态的。</p>
</blockquote>
<p><strong>缺点：</strong></p>
<ul>
<li>对象结构变化很困难： 不适用于对象结构中的类经常变化的情况，因为对象结构发生了改变，访问者的接口和访问者的实现都要发生相应的改变，代价太高。</li>
<li>破坏封装： 访问者模式通常需要对象结构开放内部数据给访问者和<code>ObjectStructrue</code>，这破坏了对象的封装性。</li>
</ul>
<h4 id="11-解释器模式-Interpreter"><a href="#11-解释器模式-Interpreter" class="headerlink" title="11 解释器模式(Interpreter)"></a>11 解释器模式(<strong>Interpreter</strong>)</h4><ul>
<li><strong>解释器模式（Interpreter Pattern）</strong>，实际应用中较少用到的行为模式。主要作用就是提供解释语言的语法或表达式的能力，从作用上来说，注定实际开发过程中会使用的少，毕竟很少有人需要构建一套自己的语法来解析吧！但是，这并不表示解释器模式我们可以忽略掉。</li>
</ul>
<p><strong>UML类图：</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200713/211053813.webp" alt="mark"></p>
<ul>
<li><strong>Context</strong>：上下文环境，包含解释器之外的全局信息</li>
<li><strong>Client</strong>：客户端，解析表达式，构建语法树，执行具体的解释操作等</li>
<li><strong>AbstractExpression</strong> ： 抽象的表达式，声明一个抽象的解释操作父类，并定义一个抽象的解释方案，其具体的实现在各个具体的子类解释其中完成。</li>
<li><strong>TerminalExpression</strong> ： 终结符表达式，实现文法中终结符有关的解释操作。文法中每一个终结符都有一个具体的中介表达式与之对应。</li>
<li><strong>NonterminalExpression</strong>：非终结表达式，实现文法中非终结符有关的解释操作。</li>
</ul>
<p>其中<code>AbstractExpression</code>的<code>interpret()</code>是抽象的解析方法，参数是上下文的环境，而<code>interpret()</code>方法的具体实现则由<code>TerminalExpression</code>和<code>NonterminalExpression</code>实现。</p>
<p><strong>具体实现：</strong></p>
<p>如下我们通过对算术表达式的解释来看一个解释器模式的实现, 如表达式<code>m+n+p</code>中,如果我们使用解释器模式对该表达式进行解释，那么<code>m</code>、<code>n</code>、<code>p</code>代表的三个字母可以看成是终结符号，而<code>+</code>代表的运算符则可以看成是非终结符号。</p>
<ul>
<li>先创建抽象的解释器，表示数学运算</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">ArithmeticExpression</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">int</span> <span class="title">interptet</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>解释器中定义了<code>interptet()</code>方法，<code>ArithmeticExpression</code>有两个子类，分别是<code>NumExpression</code>和<code>OperatorExpression</code></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** 对数字进行解释 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NumExpression</span> <span class="keyword">extends</span> <span class="title">ArithmeticExpression</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> num;</span><br><span class="line">        </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">NumExpression</span><span class="params">(<span class="keyword">int</span> num)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.num = num;</span><br><span class="line">    &#125;</span><br><span class="line">        </span><br><span class="line">    <span class="meta">@Override</span> </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">interptet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> num;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** 对运算符进行解释 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">OperatorExpression</span> <span class="keyword">extends</span> <span class="title">ArithmeticExpression</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> ArithmeticExpression mArithmeticExpression1,mArithmeticExpression2;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">OperatorExpression</span><span class="params">(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2)</span> </span>&#123;</span><br><span class="line">        mArithmeticExpression1 = arithmeticExpression1;</span><br><span class="line">        mArithmeticExpression2 = arithmeticExpression2;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>基础的类已经完成了，如果需要处理加法运算还需要继承<code>OperatorExpression</code>并实现<code>interptet()</code>方法来实现加法运算器<code>AdditionExpression</code></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AdditionExpression</span> <span class="keyword">extends</span> <span class="title">OperatorExpression</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AdditionExpression</span><span class="params">(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(arithmeticExpression1, arithmeticExpression2);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">interptet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mArithmeticExpression1.interptet() + mArithmeticExpression2.interptet();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>还差一个业务逻辑处理类</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Calculator</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//声明一个Stack栈，存储并操作所有相关的解释器</span></span><br><span class="line">    <span class="keyword">protected</span> Stack&lt;ArithmeticExpression&gt; mArithmeticExpressionStack = <span class="keyword">new</span> Stack&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Calculator</span><span class="params">(String expression)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//声明两个ArithmeticExpression类型的临时变量，存储运算符左右两边的数字解释器</span></span><br><span class="line">        ArithmeticExpression arithmeticExpression1, arithmeticExpression2;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//根据空格分隔表达式字符串</span></span><br><span class="line">        String[] elements = expression.split(<span class="string">" "</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//循环遍历表达式元素数组</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; elements.length; ++i) &#123;</span><br><span class="line">            <span class="keyword">switch</span> (elements[i].charAt(<span class="number">0</span>)) &#123;</span><br><span class="line">                <span class="comment">//如果是加号</span></span><br><span class="line">                <span class="keyword">case</span> <span class="string">'+'</span>:</span><br><span class="line">                    <span class="comment">//将栈中的解释器弹出作为运算符号左边的解释器</span></span><br><span class="line">                    arithmeticExpression1 = mArithmeticExpressionStack.pop();</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//同时将运算符号数组下标下一个元素构造为一个数字解析器</span></span><br><span class="line">                    arithmeticExpression2 = <span class="keyword">new</span> NumExpression(Integer.valueOf(elements[++i]));</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//通过上面两个数字解释器构造加法运算解释器</span></span><br><span class="line">                    mArithmeticExpressionStack.push(<span class="keyword">new</span> AdditionExpression(arithmeticExpression1, arithmeticExpression2));</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                <span class="keyword">default</span>:</span><br><span class="line">                    <span class="comment">//如果不是运算符，则是数字，直接构造数字解释器并压入栈</span></span><br><span class="line">                    mArithmeticExpressionStack.push(<span class="keyword">new</span> NumExpression(Integer.valueOf(elements[i])));</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//计算结果</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">calculate</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mArithmeticExpressionStack.pop().interptet();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里需要注意的是，为了简化逻辑，在约定的表达式的每个元素之间必须使用空格隔开，如<code>123 + 32 + 666</code>这种形式的表达式，这样才能在<code>Calculator</code>中使用空格来拆分字符串。</p>
<ul>
<li>如果想引入减法运算，我们只需要定义一个减法解释器</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SubtractionExpreesion</span> <span class="keyword">extends</span> <span class="title">OperatorExpression</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SubtractionExpreesion</span><span class="params">(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(arithmeticExpression1, arithmeticExpression2);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">interptet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mArithmeticExpression1.interptet() - mArithmeticExpression2.interptet();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在<code>Calculator</code>的<code>switch</code>中添加如下代码即可</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">case</span> <span class="string">"-"</span>:</span><br><span class="line">    arithmeticExpression1 = mArithmeticExpressionStack.pop();</span><br><span class="line">    arithmeticExpression2 = <span class="keyword">new</span> NumExpression(Integer.valueOf(elements[++i]));</span><br><span class="line">    </span><br><span class="line">    mArithmeticExpressionStack.push(<span class="keyword">new</span> SubtractionExpreesion(arithmeticExpression1, arithmeticExpression2));</span><br><span class="line">    <span class="keyword">break</span>;</span><br></pre></td></tr></table></figure>



<p><strong>总结</strong></p>
<blockquote>
<p>这里，我们能看出来解释器模式灵活性强，但是这是对于相对简单的语言；如果需要加入乘除取余等等，一并进行混合预算的话还需要考虑不同符号的运算优先级逻辑处理，所以在“简单的语言”中适用解释器模式。其实解释器模式的本质就是，将复杂的问题模块化，分离实现、解释执行。</p>
</blockquote>
<p><strong>JDK:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html" target="_blank" rel="noopener">java.util.Pattern</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html" target="_blank" rel="noopener">java.text.Normalizer</a></li>
<li>All subclasses of <a href="http://docs.oracle.com/javase/8/docs/api/java/text/Format.html" target="_blank" rel="noopener">java.text.Format</a></li>
<li><a href="http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html" target="_blank" rel="noopener">javax.el.ELResolver</a></li>
</ul>
<h2 id="三：结构型"><a href="#三：结构型" class="headerlink" title="三：结构型"></a>三：结构型</h2><h4 id="1-装饰者模式（Decorator）"><a href="#1-装饰者模式（Decorator）" class="headerlink" title="1. 装饰者模式（Decorator）"></a>1. 装饰者模式（Decorator）</h4><ul>
<li>装饰者模式（Decorator Pattern）也称为包装模式（Wrapper Pattern），以透明动态的方式来动态扩展对象的功能，也是<strong>继承关系中一种代替方案</strong>。</li>
<li><strong>装饰者模式与继承关系的目的都是要扩展对象的功能，但是装饰者模式可以提供比继承更多的灵活性。(减少代码的冗余)</strong></li>
<li><strong>通过不同具体装饰类以及这些装饰类的排列组合，设计师可以创造出很多不同行为的组合。</strong></li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200624/104532725.webp" alt="mark"></p>
<ul>
<li>Component: 抽象组件（可以是抽象类或者接口），被装饰的原始对象</li>
<li>ConcreteComponent：具体的实现类，被装饰的具体对象</li>
<li>Decorator ： 抽象的装饰者，职责就是为了装饰我们的组件对象，内部一定要有一个指向组件的对象引用。</li>
<li>ConcreteDecoratorA：装饰者具体实现类，只对抽象装饰者做出具体实现</li>
<li>ConcreteDecoratorB：同上</li>
</ul>
<p><strong>举个例子：</strong></p>
<ol>
<li>人定义为抽象类，有一个抽象方法<code>eat()</code></li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="2">
<li>创建一个<code>NormalPerson</code>类继承<code>Person</code>，对<code>eat()</code>方法有了具体实现；</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NormalPerson</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"吃饭"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="3">
<li>定义一个<code>PersonFood</code>类来表示装饰者的抽象类，保持了一个对<code>Person</code>的引用，可以方便调用具体被装饰的对象方法，这样就可以方便的对其进行扩展功能，并且不改变原类的层次结构。</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PersonFood</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Person person;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PersonFood</span><span class="params">(Person person)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.person = person;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        person.eat();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="4">
<li>接着就是具体的装饰类了，这两个类没有本质上的区别，都是为了扩展<code>PersonFood</code>类，不修改原有类的方法和结构</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ExpensiveFood</span> <span class="keyword">extends</span> <span class="title">PersonFood</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ExpensiveFood</span><span class="params">(Person person)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(person);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.eat();</span><br><span class="line">        eatSteak();</span><br><span class="line">        drinkRedWine();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eatSteak</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"吃牛排"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">drinkRedWine</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"喝拉菲"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CheapFood</span> <span class="keyword">extends</span> <span class="title">PersonFood</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">CheapFood</span><span class="params">(Person person)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(person);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eat</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.eat();</span><br><span class="line">        eatNoodles();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">eatNoodles</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"吃面条"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>Client:测试</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span>&#123;</span><br><span class="line">        Person person = <span class="keyword">new</span> NormalPerson();</span><br><span class="line"></span><br><span class="line">        PersonFood cheapFood = <span class="keyword">new</span> CheapFood(person);</span><br><span class="line">        cheapFood.eat();</span><br><span class="line"></span><br><span class="line">        PersonFood expensiveFood = <span class="keyword">new</span> ExpensiveFood(person);</span><br><span class="line">        expensiveFood.eat();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>总结：</strong></p>
<ul>
<li><h5 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h5><ul>
<li>装饰者模式与<strong>继承关系</strong>的目的都是要<strong>扩展对象的功能</strong>，但是装饰者模式可以提供比继承更多的灵活性。</li>
<li>通过使用不同的具体装饰类以及这些装饰类的排列组合，设计师可以创造出很多不同行为的<strong>组合</strong>。</li>
</ul>
</li>
</ul>
<p><strong>与代理模式的区别：</strong></p>
<ul>
<li>其实装饰着模式和代理模式很想，但两个目的不尽相同</li>
<li>装饰者模式是以对客户端透明的方式扩展对象的功能，是继承关系的一个替代方案，目的是为了减少类的冗余</li>
<li>代理模式是提供一个代理对象，并有代理对象来控制原来对象的引用。</li>
<li>总而言之：代理模式是为了对代理对象进行控制，但不做功能的扩展；装饰者模式为本装饰的对象进行功能的扩展。</li>
</ul>
<h4 id="2-桥模式（Bridge）"><a href="#2-桥模式（Bridge）" class="headerlink" title="2. 桥模式（Bridge）"></a>2. 桥模式（Bridge）</h4><ul>
<li><p>定义：将<strong>抽象部分（业务功能</strong>）与<strong>实现部分（平台实现）</strong>分离，使他们可以独立的变化。<strong>（主要是违背了单一职责原则）</strong></p>
</li>
<li><p>动机：由于某些类型的固有的实现逻辑，使得他们具有两个变化的维度，乃至多个变化的维度</p>
<ul>
<li>使用在对象间的<strong>组合关系</strong>，使得抽象和实现之间沿着各自的维度来变化。</li>
</ul>
</li>
</ul>
<p><strong>类图：</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200626/091251315.png" alt="mark"></p>
<ul>
<li>Abstraction：定义抽象类的接口</li>
<li>Implementor：定义实现类接口</li>
</ul>
<p><strong>代码实现：</strong></p>
<ul>
<li>RemoteControl 表示遥控器，指代 Abstraction。</li>
<li>TV 表示电视，指代 Implementor。</li>
<li>桥接模式将遥控器和电视分离开来，从而可以独立改变遥控器或者电视的实现。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">TV</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">on</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">off</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">tuneChannel</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>TV具体实现：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Sony</span> <span class="keyword">extends</span> <span class="title">TV</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">on</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"Sony On"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">off</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"Sony Off"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">tuneChannel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"Sony ChannelChange"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ChangHong</span> <span class="keyword">extends</span> <span class="title">TV</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">on</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ChangHong On"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">off</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ChangHong Off"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">tuneChannel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ChangHong ChannelChange"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>遥控器：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">RemoteController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 组合tv</span></span><br><span class="line">    <span class="keyword">protected</span> TV tv;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 指向tv的指针</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">RemoteController</span><span class="params">(TV tv)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.tv = tv;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">on</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">off</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">tuneChannel</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>具体遥控器：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteRemote1</span> <span class="keyword">extends</span> <span class="title">RemoteController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 指向tv的指针</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ConcreteRemote1</span><span class="params">(TV tv)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(tv);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">on</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ConcreteRemoteControl1 on"</span>);</span><br><span class="line">        tv.on();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">off</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ConcreteRemoteControl1 off"</span>);</span><br><span class="line">        tv.off();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">tuneChannel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ConcreteRemoteControl1 tuneChannel"</span>);</span><br><span class="line">        tv.tuneChannel();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConcreteRemote2</span> <span class="keyword">extends</span> <span class="title">RemoteController</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 指向TV的指针</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ConcreteRemote2</span><span class="params">(TV tv)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(tv);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">on</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ConcreteRemoteControl2 on"</span>);</span><br><span class="line">        tv.on();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">off</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ConcreteRemoteControl2 off"</span>);</span><br><span class="line">        tv.off();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">tuneChannel</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"ConcreteRemoteControl2 tuneChannel"</span>);</span><br><span class="line">        tv.tuneChannel();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>客户端测试：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 组合的方式</span></span><br><span class="line">        ConcreteRemote1 remote1 = <span class="keyword">new</span> ConcreteRemote1(<span class="keyword">new</span> ChangHong());</span><br><span class="line">        remote1.on();</span><br><span class="line">        remote1.off();</span><br><span class="line">        remote1.tuneChannel();</span><br><span class="line">        System.out.println(remote1.tv);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"====================="</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 组合的方式</span></span><br><span class="line">        ConcreteRemote2 remote2 = <span class="keyword">new</span> ConcreteRemote2(<span class="keyword">new</span> Sony());</span><br><span class="line">        remote2.on();</span><br><span class="line">        remote2.off();</span><br><span class="line">        remote2.tuneChannel();</span><br><span class="line">        System.out.println(remote2.tv);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//结果：</span></span><br><span class="line">ConcreteRemoteControl1 on</span><br><span class="line">ChangHong On</span><br><span class="line">ConcreteRemoteControl1 off</span><br><span class="line">ChangHong Off</span><br><span class="line">ConcreteRemoteControl1 tuneChannel</span><br><span class="line">ChangHong ChannelChange</span><br><span class="line">ChangHong@<span class="number">4554617</span>c</span><br><span class="line">=====================</span><br><span class="line">ConcreteRemoteControl2 on</span><br><span class="line">Sony On</span><br><span class="line">ConcreteRemoteControl2 off</span><br><span class="line">Sony Off</span><br><span class="line">ConcreteRemoteControl2 tuneChannel</span><br><span class="line">Sony ChannelChange</span><br><span class="line">Sony@<span class="number">74</span>a14482</span><br></pre></td></tr></table></figure>



<p><strong>JDK中实现</strong></p>
<ul>
<li>AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)</li>
<li>JDBC</li>
</ul>
<h4 id="3-享元模式（Flyweight）"><a href="#3-享元模式（Flyweight）" class="headerlink" title="3. 享元模式（Flyweight）"></a>3. 享元模式（Flyweight）</h4><ul>
<li><p><strong>享元模式</strong> ： 是对象池的一种实现，主要用于减少创建对象的数量，<strong>以减少内存占用和提供性能。</strong></p>
</li>
<li><p>定义：运用共享技术有效的支持大量细粒度的对象<strong>（要求对象是只读的）</strong></p>
</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200628/111500446.webp" alt="mark"></p>
<ul>
<li>Flyweight:  享元模式的抽象类或者接口。</li>
<li>ConcreateFlyweight: 具体的享元对象。</li>
<li>FlyweightFactory：享元工厂，负责管理享元对象池和创建享元对象。</li>
</ul>
<p><strong>代码实现</strong></p>
<ul>
<li>场景：过年抢票，大家肯定都不陌生，各种刷票插件、软件什么的。在用户设置好出发和到达之后，每次查询请求都返回一系列的车票结果。<ul>
<li>当数千万的用户在不断请求查询结果时，如果每次查询结果都是重新创建返回的，可想而知，肯定会有大量的重复对象的创建、销毁，内存占用和GC的压力都会随之增大。</li>
<li>而享元模式就能很好的应对这种情况，车次是固定的，根据出发地和到达地查询出来的车次基本都是相同的</li>
</ul>
</li>
</ul>
<ol>
<li>创建一个Ticket接口，定义输出车票信息的方法</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建一个Ticket接口，定义输出车票信息的方法</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Ticket</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showTicket</span><span class="params">(String info)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol start="2">
<li>具体的实现类</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TrainTicket</span> <span class="keyword">implements</span> <span class="title">Ticket</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//出发地</span></span><br><span class="line">    <span class="keyword">private</span>  String from;</span><br><span class="line">    <span class="comment">//到达地</span></span><br><span class="line">    <span class="keyword">private</span>  String to;</span><br><span class="line">    <span class="comment">//铺位</span></span><br><span class="line">    <span class="keyword">private</span> String bunk;</span><br><span class="line">    <span class="comment">//价格</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> price;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">TrainTicket</span><span class="params">(String from, String to)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.from = from;</span><br><span class="line">        <span class="keyword">this</span>.to = to;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showTicket</span><span class="params">(String info)</span> </span>&#123;</span><br><span class="line">        price = <span class="keyword">new</span> Random().nextInt(<span class="number">200</span>);</span><br><span class="line">        System.out.println(<span class="string">"查询 从 "</span>+from+<span class="string">" 到 "</span> + to + <span class="string">" 的 "</span> + bunk + <span class="string">" 车票，价格："</span> + price);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol start="3">
<li>享元工厂</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TicketFactory</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// JUC MAP</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Map&lt;String,Ticket&gt; ticketMap = <span class="keyword">new</span> ConcurrentHashMap&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 每次获取车票对象时的创建，享元模式有效的减少了重复对象的创建。</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Ticket <span class="title">getTicket</span><span class="params">(String from,String to)</span></span>&#123;</span><br><span class="line">        <span class="keyword">final</span> String key = from + <span class="string">"-"</span> + to;</span><br><span class="line">        <span class="comment">// 享元模式的精髓，有就直接返回这个对象</span></span><br><span class="line">        <span class="keyword">if</span> (ticketMap.containsKey(key))&#123;</span><br><span class="line">            <span class="keyword">return</span> ticketMap.get(key);</span><br><span class="line">        &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 没有就加入到对象池中，并且返回这个对象</span></span><br><span class="line">            TrainTicket ticket = <span class="keyword">new</span> TrainTicket(from, to);</span><br><span class="line">            ticketMap.put(key,ticket);</span><br><span class="line">            <span class="keyword">return</span> ticket;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>JDK:</strong></p>
<p>Java 利用缓存来加速大量小对象的访问时间。</p>
<ul>
<li>java.lang.Integer#valueOf(int)</li>
<li>java.lang.Boolean#valueOf(boolean)</li>
<li>java.lang.Byte#valueOf(byte)</li>
<li>java.lang.Character#valueOf(char)</li>
</ul>
<h4 id="4-外观模式（Facade）"><a href="#4-外观模式（Facade）" class="headerlink" title="4. 外观模式（Facade）"></a>4. 外观模式（Facade）</h4><ul>
<li>外观模式：封装系统的复杂结构，向外提供一个可以访问系统的接口，这个接口就是系统内外通信的统一的出入口。这样使得系统更加易于维护和使用<ul>
<li>在我们集成很多第三方SDK的时候就会发现，我们集成一次之后，想要升级SDK的版本的话，我们只需要替换jar或者修改依赖库的版本，当然你也可以一直使用最新的版本。</li>
</ul>
</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200628/113712188.webp" alt="mark"></p>
<ul>
<li>Client：客户端，直接使用<code>Facade</code>接口提供的方法</li>
<li>Facade：就是系统对外的同一对象，封装了各个子系统的交互简化了<code>Client</code>调用</li>
<li>SystemA、SystemB、SystemC：子系统接口</li>
<li>ConcreteSystemA、ConcreteSystemB、ConcreteSystemC：子系统的实现</li>
</ul>
<p>这里就可以看出，对Client来说只需要知道一个Facade一个就行，不需要知道Facade内部的具体复杂逻辑，降低用户的使用成本。<strong>（思想：高内聚，松耦合）</strong></p>
<p><strong>优点</strong></p>
<ul>
<li>对使用者隐藏内部的具体细节，降低使用者和子系统的耦合。</li>
<li>外观类对子系统的接口封装，使得系统更加容易使用</li>
</ul>
<p><strong>缺点</strong></p>
<ul>
<li>外观类接口会过于庞大</li>
<li>没有遵循开闭原则，业务变化时需要修改外观类</li>
</ul>
<p><strong>代码实现：没有特定代码结构</strong></p>
<ul>
<li>场景：生活中有很多这样的例子，我们经常使用的智能手机就是一个外观模式的例子，能够打电话、拍照等功能，而打电话和拍照又是独立的功能系统。</li>
</ul>
<ol>
<li>手机功能</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Phone</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">call</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">hangUp</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PhomeImpl</span> <span class="keyword">implements</span> <span class="title">Phone</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">call</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"打电话"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">hangUp</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"挂电话"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="2">
<li>拍照功能</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Camera</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">takePhoto</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SnoyCamera</span> <span class="keyword">implements</span> <span class="title">Camera</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">open</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"开启相机"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">takePhoto</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"拍照"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"关闭相机"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol start="3">
<li>组装手机</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SmartPhone</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> PhomeImpl phome = <span class="keyword">new</span> PhomeImpl();</span><br><span class="line">    <span class="keyword">private</span> SnoyCamera camera = <span class="keyword">new</span> SnoyCamera();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">call</span><span class="params">()</span></span>&#123;</span><br><span class="line">        phome.call();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">takePhoto</span><span class="params">()</span></span>&#123;</span><br><span class="line">        camera.open();</span><br><span class="line">        camera.takePhoto();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="5-代理模式（Proxy）"><a href="#5-代理模式（Proxy）" class="headerlink" title="5. 代理模式（Proxy）"></a>5. 代理模式（Proxy）</h4><ul>
<li><strong>代理模式</strong>： 为其他对象提供一个代理，并由代理对象控制原有对象的引用。也称作委托模式</li>
<li>其实代理模式无论是在日常开发中还是设计模式汇总都随处可见，中介者模式的中介者对象也是<strong>代理模式</strong>的应用，其他的对象的交互都是交给终结者对象处理的。而在生活中就更多的类似代理模式的例子。例如：抢票插件，科学上网等等。</li>
</ul>
<p><strong>UML类图：</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200629/091646377.webp" alt="mark"></p>
<ul>
<li>subject: 抽象主体类（也可以是接口），声明共同的功能</li>
<li>RealSubject: 真实的主体类，也就是<strong>被代理的类</strong>，负责执行具体的业务逻辑方法；客户类调用代理类间接调用其定义的方法。</li>
<li>ProxySubject:代理主体类，也就是<strong>代理类</strong>，持有一个被代理类的真实对象，在实现抽象主体类共同方法中调用<strong>被代理类</strong>的相对应的方法，起到代理的作用。</li>
<li>Client: 客户类，直接去访问代理</li>
</ul>
<h5 id="5-1-静态代理"><a href="#5-1-静态代理" class="headerlink" title="5.1 静态代理"></a>5.1 静态代理</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**抽象主体*/</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Subject</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**被代理主体*/</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RealSubject</span> <span class="keyword">implements</span> <span class="title">Subject</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">"this is real subject"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**代理主题*/</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ProxySubject</span> <span class="keyword">implements</span> <span class="title">Subject</span></span>&#123;</span><br><span class="line">    <span class="comment">// 真实主体</span></span><br><span class="line">    <span class="keyword">private</span> RealSubject realSubject;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ProxySubject</span><span class="params">(RealSubject realSubject)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.realSubject = realSubject;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">visit</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.print(<span class="string">"proxy start"</span>);</span><br><span class="line">        realSubject.visit();</span><br><span class="line">        System.out.print(<span class="string">"proxy end"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**客户类*/</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Client</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建被代理对象</span></span><br><span class="line">        RealSubject realSubject = <span class="keyword">new</span> RealSubject();</span><br><span class="line">        <span class="comment">// 创建代理对象</span></span><br><span class="line">        ProxySubject proxySubject = <span class="keyword">new</span> ProxySubject(realSubject);</span><br><span class="line">        <span class="comment">// 调用方法</span></span><br><span class="line">        proxySubject.visit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>静态代理的好处:</strong></p>
<ul>
<li>可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .</li>
<li>公共的业务由代理来完成 . 实现了业务的分工 ,</li>
<li>公共业务发生扩展时变得更加集中和方便 .</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li>类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .</li>
</ul>
<h5 id="5-2-动态代理"><a href="#5-2-动态代理" class="headerlink" title="5.2 动态代理"></a>5.2 动态代理</h5><ul>
<li>上一部分介绍的，其实就是<strong>静态代理，也就是在代码的编译阶段生成代理类来完成代理对象的一系列操作。</strong></li>
<li>而<strong>动态代理</strong>则是在运行时动态生成代理类对象。</li>
<li>代理对象的生成则是利用<code>JDK</code>中的<code>java.lang.reflect.Proxy</code> 类 ，使用<code>newProxyInstance</code> 方法则可以创建一个我们所需要代理的对象</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 主要是newProxyInstance方法，动态生成代理类</span></span><br><span class="line"><span class="meta">@CallerSensitive</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Object <span class="title">newProxyInstance</span><span class="params">(ClassLoader loader,</span></span></span><br><span class="line"><span class="function"><span class="params">                                          Class&lt;?&gt;[] interfaces,</span></span></span><br><span class="line"><span class="function"><span class="params">                                          InvocationHandler h)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> IllegalArgumentException</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Objects.requireNonNull(h);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">final</span> Class&lt;?&gt;[] intfs = interfaces.clone();</span><br><span class="line">        <span class="keyword">final</span> SecurityManager sm = System.getSecurityManager();</span><br><span class="line">        <span class="keyword">if</span> (sm != <span class="keyword">null</span>) &#123;</span><br><span class="line">            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * Look up or generate the designated proxy class.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        Class&lt;?&gt; cl = getProxyClass0(loader, intfs);</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * Invoke its constructor with the designated invocation handler.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (sm != <span class="keyword">null</span>) &#123;</span><br><span class="line">                checkNewProxyPermission(Reflection.getCallerClass(), cl);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">final</span> Constructor&lt;?&gt; cons = cl.getConstructor(constructorParams);</span><br><span class="line">            <span class="keyword">final</span> InvocationHandler ih = h;</span><br><span class="line">            <span class="keyword">if</span> (!Modifier.isPublic(cl.getModifiers())) &#123;</span><br><span class="line">                AccessController.doPrivileged(<span class="keyword">new</span> PrivilegedAction&lt;Void&gt;() &#123;</span><br><span class="line">                    <span class="function"><span class="keyword">public</span> Void <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                        cons.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> cons.newInstance(<span class="keyword">new</span> Object[]&#123;h&#125;);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalAccessException|InstantiationException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> InternalError(e.toString(), e);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InvocationTargetException e) &#123;</span><br><span class="line">            Throwable t = e.getCause();</span><br><span class="line">            <span class="keyword">if</span> (t <span class="keyword">instanceof</span> RuntimeException) &#123;</span><br><span class="line">                <span class="keyword">throw</span> (RuntimeException) t;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> InternalError(t.toString(), t);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchMethodException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> InternalError(e.toString(), e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>



<p><code>newProxyInstance</code> 是一个<code>Proxy</code>的静态方法，并且接收三个参数</p>
<ul>
<li><code>ClassLoader loader</code> <strong>类加载器</strong></li>
<li><code>Class&lt;?&gt;[] interfaces</code> <strong>目标对象实现的接口类型</strong></li>
<li><code>InvocationHandler h</code> 处理事件的对象，<code>InvocationHandler</code> 是一个接口，执行目标对象的方法，会触发<code>InvocationHandler</code> 的<code>invoke</code></li>
</ul>
<p><strong>另外一个重要的类当然就是<code>InvocationHandler</code> 了，其中最重要的方法就是<code>invoke</code></strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">InvocationHandler</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> Throwable</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li><code>Object proxy</code>: 执行方法的代理对象</li>
<li><code>Method method</code> ：被执行的方法的对象</li>
<li><code>bject[] args</code> : 被执行方法的参数</li>
<li>返回值：代理对象调用方法所返回的值</li>
</ul>
<p>依然使用<code>Subject</code>作为例子，看看简单的代码实现（代码没有封装）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Client</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建被代理对象</span></span><br><span class="line">        <span class="keyword">final</span> RealSubject realSubject = <span class="keyword">new</span> RealSubject();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建被代理对象</span></span><br><span class="line">        Subject subject = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),</span><br><span class="line">                <span class="keyword">new</span> Class[]&#123;Subject<span class="class">.<span class="keyword">class</span>&#125;,</span></span><br><span class="line"><span class="class">                <span class="title">new</span> <span class="title">InvocationHandler</span>() </span>&#123;</span><br><span class="line">                    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">                        <span class="comment">// 第一个参数是要代理的对象</span></span><br><span class="line">                        <span class="comment">// 第二个参数是需要传入的参数</span></span><br><span class="line">                        <span class="keyword">return</span> method.invoke(realSubject,args);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 调用方法</span></span><br><span class="line">        subject.visit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>从代码上来看，比之前静态代理要简单很多，没有那么多类和对象，但是<strong>相对的代码在性能上有牺牲</strong>，而且对于不太熟悉反射相关的知识的开发者并不是太友好。</li>
<li>通过反射类<code>Proxy</code> 和 <code>InvocationHandler</code> 回调接口实现JDK动态代理，要求被代理类必须实现一个接口，但事实上并不是所有的类都有接口，对于没有实现接口的类，便无法使用该方法实现动态代理。</li>
</ul>
<h5 id="5-3-其他代理分类"><a href="#5-3-其他代理分类" class="headerlink" title="5.3 其他代理分类"></a>5.3 其他代理分类</h5><p>静态代理和动态代理是代码方法来区分代理模式，也可以从使用范围来区分不同的代理实现</p>
<ul>
<li>远程代理（Remote Proxy）: 为某个对象在不同的内存地址空间提供局部代理。使得系统可以将<code>Server</code> 部分的实现隐藏，以便<code>Client</code>不必考虑<code>Server</code>的存在</li>
<li>虚拟代理（Virtual Proxy） : 使用一个代理对象表示一个十分耗资源的对象并在真正需要时候才创建</li>
<li>保护代理(Protection Proxy): 使用代理控制对原始对象的访问.该类型的代理常被用于原始对象有不同访问权限的情况.</li>
<li>智能引用(Smart Reference) : 在访问原始对象时执行一些自己的附加操作并对指向原始对象的引用计数.</li>
</ul>
<p>这里要注意的是，静态和动态代理都可以应用于上述4种情形，两者是各自独立的变化。</p>
<p><strong>总结:</strong></p>
<p>代理模式使用非常广泛，基本在其他的设计模式中也能看到代理模式的影子，但是使用时针对性较强，而且模式本身并没有什么突出的优缺点，基本上可以放心使用</p>
<p><strong>通用的动态代理实现类:</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConfigProxy</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 要代理的对象</span></span><br><span class="line">    <span class="keyword">private</span> Object target;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setTarget</span><span class="params">(Object target)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.target = target;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 生成代理类</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getProxy</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Proxy.newProxyInstance(</span><br><span class="line">                <span class="keyword">this</span>.getClass().getClassLoader(),</span><br><span class="line">                target.getClass().getInterfaces(),</span><br><span class="line">                <span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// proxy ： 代理类</span></span><br><span class="line">    <span class="comment">// method : 代理类中要代理的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        <span class="comment">// ....</span></span><br><span class="line">        <span class="comment">// 业务逻辑代码</span></span><br><span class="line">        <span class="keyword">return</span> method.invoke(target,args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<p><strong>JDK:</strong></p>
<ul>
<li>java.lang.reflect.proxy</li>
<li>java.lang.reflect.InvocationHandler</li>
<li>Spring的 Aop</li>
</ul>
<h4 id="6-适配器模式（Adapter）"><a href="#6-适配器模式（Adapter）" class="headerlink" title="6. 适配器模式（Adapter）"></a>6. 适配器模式（Adapter）</h4><p>定义：此模式的作用就是兼容原本接口不匹配的两个类，起到桥梁的作用。</p>
<h5 id="6-1-类的适配器模式"><a href="#6-1-类的适配器模式" class="headerlink" title="6.1 类的适配器模式"></a>6.1 类的适配器模式</h5><ul>
<li>类的适配器模式：采用继承实现</li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200701/085746124.webp" alt="mark"></p>
<ul>
<li>Target：目标接口，也就是期望得到的接口</li>
<li>Adaptee：需要适配的接口</li>
<li>Adapter：适配器，负责把<code>Adaptee</code>转换成<code>Target</code>的类</li>
</ul>
<p>不说远了，就说<strong>手机充电头</strong>，其实就是一个电源适配器，生活用电的电压是220v，而手机充电的电压是5v，所以这时候就需要使用到电源适配器将220v的电压转换为5v（如果你是直接插在插板的USB接口上的话，当我没说）。</p>
<p>对应上面的UML图， 生活电压就是<code>Adaptee</code>，手机充电电压就是<code>Target</code>，不用多说，电源适配器自然就是<code>Adapter</code>了。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Target接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Volt5</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getVolt5</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Adaptee类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Volt220</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getVlot220</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">220</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Adapter类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">VoltAdapter</span> <span class="keyword">extends</span> <span class="title">Volt220</span> <span class="keyword">implements</span> <span class="title">Volt5</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getVolt5</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Client</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        VoltAdapter adapter = <span class="keyword">new</span> VoltAdapter();</span><br><span class="line">        System.out.println(<span class="string">"输出电压"</span> + adapter.getVolt5());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h5 id="6-2-对象适配器模式"><a href="#6-2-对象适配器模式" class="headerlink" title="6.2 对象适配器模式"></a>6.2 对象适配器模式</h5><ul>
<li>与类适配器不同的是，<strong>对象适配器模式不使用继承关联链接<code>Adaptee</code> 类，而是使用代理关系连接到<code>Adaptee</code>类</strong></li>
</ul>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200701/090602860.webp" alt="mark"></p>
<ul>
<li>这种实现方式直接将要被适配的对象传递到<code>Adapter</code> 中，使用组合的形式实现接口的兼容效果。</li>
<li>一般常用的适配器模式都是对象适配器模式</li>
<li>还有就是<code>Adaptee</code> 对象不会被暴露出来，因为没有继承被适配的对象。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Target接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Volt5</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getVolt5</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//Adaptee类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Volt220</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getVlot220</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">220</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Adapter类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">VoltAdapter</span> <span class="keyword">implements</span> <span class="title">Volt5</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 组合的形式</span></span><br><span class="line">    Volt220 volt220;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">VoltAdapter</span><span class="params">(Volt220 volt220)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.volt220 = volt220;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getVolt5</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">5</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getVolt220</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> volt220.getVlot220();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Client</span></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        VoltAdapter adapter = <span class="keyword">new</span> VoltAdapter(<span class="keyword">new</span> Volt220());</span><br><span class="line">        System.out.println(<span class="string">"输出电压"</span> + adapter.getVolt5());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>JDK:</strong></p>
<ul>
<li><a href="http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29" target="_blank" rel="noopener">java.util.Arrays#asList()</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-" target="_blank" rel="noopener">java.util.Collections#list()</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-" target="_blank" rel="noopener">java.util.Collections#enumeration()</a></li>
<li><a href="http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-" target="_blank" rel="noopener">javax.xml.bind.annotation.adapters.XMLAdapter</a></li>
</ul>
<h4 id="7-组合模式-Composite"><a href="#7-组合模式-Composite" class="headerlink" title="7. 组合模式(Composite)"></a>7. 组合模式(Composite)</h4><ul>
<li><strong>组合模式(Composite Pattern)</strong>，也称作部分整体模式(Part-Whole Pattern)</li>
<li><strong>定义：将一组相似的对象看作一个对象来处理，并根据一个树状结构来组合对象；对象都提供一个统一的方法去访问相应的对象来处理多个对象的同一性的问题</strong></li>
</ul>
<blockquote>
<p>组合模式属于结构设计模式之一，而其设计目的就是将对象组合成树形结构以表示“部分-整体”的层次结构，使得用户对单个对象的组合对象使用具有一致性；所以决定组合模式的设计基础就是树状结构。</p>
<p>组合模式所使用的情况也就是树状结构或者适用树状结构来解决问题的情况。</p>
</blockquote>
<p><strong>UML类图</strong></p>
<p><img src="http://zhuuu-bucket.oss-cn-beijing.aliyuncs.com/img/20200704/084505772.webp" alt="mark"></p>
<ul>
<li>Component：抽象节点，为组合中的对象声明统一接口。</li>
<li>Composite：可以储存子节点的节点对象，并实现抽象节点的有关操作。</li>
<li>Leaf：叶子节点，没有子节点的对象了。</li>
<li>Client：组合节点对象，进行操作</li>
</ul>
<p>注意：</p>
<ul>
<li>这里所描述的是<strong>透明的组合模式</strong>，可以看到<code>Component</code>类中除了统一的操作方法<code>doSomthing()</code>方法以外，还有操作子节点的相关方法，<strong>而叶子节点<code>Leaf</code>类定义就是没有叶子节点的，显然这些操作子节点的方法就是多余的。</strong></li>
<li><strong>如果要让<code>leaf</code>类不继承这些方法，只能将<code>compoent</code> 类中的这些方法放到<code>composite</code>类中，然而这样的设计方式与依赖倒置原则相违背，所以这里没有采用这种组合模式（安全的组合模式）</strong></li>
</ul>
<p><strong>代码实现：</strong></p>
<ul>
<li>说到<strong>组合模式</strong>，最适合的就是文件系统的结构了，文件夹中就子文件夹和文件，子文件夹中可能又是如此，典型的树状结构。</li>
<li>抽象的目录类，有目录名，有输出目录名，并提供添加目录，删除目录和清除目录的方法</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Directory</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 当前目录名</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Directory</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 输出目录结构</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 添加一个文件或者文件夹</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> dir</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">addDir</span><span class="params">(Directory dir)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除一个文件或者文件夹</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> dir</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">removeDir</span><span class="params">(Directory dir)</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 清空目录</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取目录中的所有目录</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> List&lt;Directory&gt; <span class="title">getDirs</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取目录名</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><strong>文件夹类</strong>，申明一个集合存储自身的目录，并实现具体的操作方法，在<code>print()</code>方法中循环调用集合中目录的<code>print()</code>方法输出</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Folder</span> <span class="keyword">extends</span> <span class="title">Directory</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 当前文件夹下的所有目录元素</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">protected</span> List&lt;Directory&gt; directories = <span class="keyword">new</span> ArrayList&lt;Directory&gt;();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Folder</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 打印文件目录</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(getName() + <span class="string">"["</span>);</span><br><span class="line">        Iterator&lt;Directory&gt; iterator = directories.iterator();</span><br><span class="line">        <span class="keyword">while</span> (iterator.hasNext())&#123;</span><br><span class="line">            Directory directory = iterator.next();</span><br><span class="line">            directory.print();</span><br><span class="line">            <span class="keyword">if</span> (iterator.hasNext())&#123;</span><br><span class="line">                System.out.println(<span class="string">","</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"]"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addDir</span><span class="params">(Directory dir)</span> </span>&#123;</span><br><span class="line">        directories.add(dir);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">removeDir</span><span class="params">(Directory dir)</span> </span>&#123;</span><br><span class="line">        directories.remove(dir);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        directories.clear();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;Directory&gt; <span class="title">getDirs</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> directories;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li><strong>文件类</strong>：实现了<code>print()</code>方法，由于没有子目录，相关操作的方法都抛出异常</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">File</span> <span class="keyword">extends</span> <span class="title">Directory</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">File</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">print</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(getName());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addDir</span><span class="params">(Directory dir)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException(<span class="string">"文件对象不支持该操作"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">removeDir</span><span class="params">(Directory dir)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException(<span class="string">"文件对象不支持该操作"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException(<span class="string">"文件对象不支持该操作"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> List&lt;Directory&gt; <span class="title">getDirs</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException(<span class="string">"文件对象不支持该操作"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ul>
<li>测试类：模拟输出C盘的结构</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Directory root = <span class="keyword">new</span> Folder(<span class="string">"C"</span>);</span><br><span class="line">        root.addDir(<span class="keyword">new</span> Folder(<span class="string">"windows"</span>));</span><br><span class="line">        Directory program = <span class="keyword">new</span> Folder(<span class="string">"Program File(x86)"</span>);</span><br><span class="line">        program.addDir(<span class="keyword">new</span> Folder(<span class="string">"Intellij"</span>));</span><br><span class="line">        program.addDir(<span class="keyword">new</span> File(<span class="string">"cache"</span>));</span><br><span class="line"></span><br><span class="line">        root.addDir(<span class="keyword">new</span> Folder(<span class="string">"windows"</span>));</span><br><span class="line">        root.addDir(<span class="keyword">new</span> File(<span class="string">"log.txt"</span>));</span><br><span class="line">        root.addDir(<span class="keyword">new</span> File(<span class="string">"null.txt"</span>));</span><br><span class="line"></span><br><span class="line">        root.print();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>总结：</strong></p>
<p>组合模式和解释器模式有一定的类同，都涉及递归调用。</p>
<p>但是组合模式所提供的属性层次结构使得可以同等对象单个对象和多个对象。</p>
<p>不过是以牺牲单一原则换来的，而组合模式是通过继承来实现的，这样缺少了些扩展性。</p>
<p><strong>优点</strong></p>
<p>高层模块可以一致的使用一个组合结构或者其中单个对象，不必关心处理的单个对象还是整个组合结构，简化代码</p>
<p>对于枝干构件和叶子构件的新增很方便</p>
<p>通过枝干对象和叶子对象的递归组合，可以形成复杂的树形结构，同时保持简单的方式进行控制</p>
<p><strong>JDK:</strong></p>
<ul>
<li>javax.swing.JComponent#add(Component)</li>
<li>java.awt.Container#add(Component)</li>
<li>java.util.Map#putAll(Map)</li>
<li>java.util.List#addAll(Collection)</li>
<li>java.util.Set#addAll(Collection)</li>
</ul>
<p><strong>简书参考：</strong></p>
<p><strong>参考博客</strong>：<a href="https://www.jianshu.com/u/70bd9fefe61f" target="_blank" rel="noopener">https://www.jianshu.com/u/70bd9fefe61f</a></p>

      
      <!-- reward -->
      
      <div id="reward-btn">
        打赏
      </div>
      
    </div>
      <!-- copyright -->
      
        <div class="declare">
          <ul class="post-copyright">
            <li>
              <i class="ri-copyright-line"></i>
              <strong>版权声明： </strong s>
              本博客所有文章除特别声明外，均采用 <a href="https://www.apache.org/licenses/LICENSE-2.0.html" rel="external nofollow"
                target="_blank">Apache License 2.0</a> 许可协议。转载请注明出处！
            </li>
          </ul>
        </div>
        
    <footer class="article-footer">
      
          
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://zhuuu.work/2020/06/09/GoF23/23%E7%A7%8D%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E8%AF%A6%E7%BB%86%E6%80%BB%E7%BB%93/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" rel="tag">设计模式</a></li></ul>


    </footer>

  </div>

  
  
  <nav class="article-nav">
    
      <a href="/2020/06/10/Leetcode/Leetcode-191-%E4%BD%8D1%E7%9A%84%E4%B8%AA%E6%95%B0/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            Leetcode-191-位1的个数
          
        </div>
      </a>
    
    
      <a href="/2020/06/09/Leetcode/Leetcode-189-%E6%97%8B%E8%BD%AC%E6%95%B0%E7%BB%84/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">Leetcode-189-旋转数组</div>
      </a>
    
  </nav>


  

  
  
<!-- valine评论 -->
<div id="vcomments-box">
    <div id="vcomments">
    </div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src='https://cdn.jsdelivr.net/npm/valine@1.3.10/dist/Valine.min.js'></script>
<script>
    new Valine({
        el: '#vcomments',
        notify: false,
        verify: '',
        app_id: '',
        app_key: '',
        path: window.location.pathname,
        avatar: 'mp',
        placeholder: '给我的文章加点评论吧~',
        recordIP: true
    });
    const infoEle = document.querySelector('#vcomments .info');
    if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
        infoEle.childNodes.forEach(function (item) {
            item.parentNode.removeChild(item);
        });
    }
</script>
<style>
    #vcomments-box {
        padding: 5px 30px;
    }

    @media screen and (max-width: 800px) {
        #vcomments-box {
            padding: 5px 0px;
        }
    }

    #vcomments-box #vcomments {
        background-color: #fff;
    }

    .v .vlist .vcard .vh {
        padding-right: 20px;
    }

    .v .vlist .vcard {
        padding-left: 10px;
    }
</style>

  

  
  
<div class="gitalk" id="gitalk-container"></div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1.5.0/dist/gitalk.css">


<script src="https://cdn.jsdelivr.net/npm/gitalk@1.5.0/dist/gitalk.min.js"></script>


<script src="https://cdn.jsdelivr.net/npm/blueimp-md5@2.10.0/js/md5.min.js"></script>

<script type="text/javascript">
  var gitalk = new Gitalk({
    clientID: 'db188ed8c86dc4b0dbf3',
    clientSecret: 'a58f92160e5a9efd726b7d533000a0737f3e3f3e',
    repo: 'Blog-comments',
    owner: 'Zhuuuuuuuu',
    admin: ['Zhuuuuuuuu'],
    // id: location.pathname,      // Ensure uniqueness and length less than 50
    id: md5(location.pathname),
    distractionFreeMode: false,  // Facebook-like distraction free mode
    pagerDirection: 'last'
  })

  gitalk.render('gitalk-container')
</script>

  

</article>
</section>
      <footer class="footer">
  <div class="outer">
    <ul class="list-inline">
      <li>
        &copy;
        2019-2021
        Zhuuu
      </li>
      <li>
        
      </li>
    </ul>
    <ul class="list-inline">
      <li>
        
        
        <span>
  <i>PV:<span id="busuanzi_value_page_pv"></span></i>
  <i>UV:<span id="busuanzi_value_site_uv"></span></i>
</span>
        
      </li>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
    <div class="to_top">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>
      </div>
    </main>
      <aside class="sidebar">
        <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="朱酱酱的学习博客"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/JVM/">JVM</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/JDK%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/">JDK源码</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/">多线程</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/Mysql/">Mysql</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/Redis/">Redis</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E8%AE%BE%E8%AE%A1%E8%80%85%E6%A8%A1%E5%BC%8F/">设计模式</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/MyBatis/">MyBatis</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/SpringMVC/">SpringMVC</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/Spring/">Spring</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/SpringBoot/">SpringBoot</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/">Linux</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/Leetcode/">Leetcode</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E5%89%8D%E7%AB%AF/">前端</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/">网络编程</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags/photoshop/">photoshop</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="http://smartzhuuu.lofter.com/" target="_blank" rel="noopener">摄影</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2020/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="Search">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
      </aside>
      <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/images/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/images/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
      
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/jquery.justifiedGallery.min.js"></script>


<script src="/js/lazyload.min.js"></script>


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<script src="/js/share.js"></script>



<script src="/fancybox/jquery.fancybox.min.js"></script>




<script>
  try {
    var typed = new Typed("#subtitle", {
    strings: ['昨夜西风凋碧树。独上高楼，望尽天涯路','衣带渐宽终不悔，为伊消得人憔悴。','众里寻他千百度。蓦然回首，那人却在，灯火阑珊处。'],
    startDelay: 0,
    typeSpeed: 200,
    loop: true,
    backSpeed: 100,
    showCursor: true
    });
  } catch (err) {
  }
  
</script>




<script src="/js/tocbot.min.js"></script>

<script>
  // Tocbot_v4.7.0  http://tscanlin.github.io/tocbot/
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer:'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto',
    onClick: (e) => {
      $('.toc-link').removeClass('is-active-link');
      $(`a[href=${e.target.hash}]`).addClass('is-active-link');
      $(e.target.hash).scrollIntoView();
      return false;
    }
  });
</script>


<script>
  var ayerConfig = {
    mathjax: true
  }
</script>


<script src="/js/ayer.js"></script>


<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">


<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>



<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
      }
  });

  MathJax.Hub.Queue(function() {
      var all = MathJax.Hub.getAllJax(), i;
      for(i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
      }
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.6/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>


<script type="text/javascript" src="https://js.users.51.la/20544303.js"></script>
  </div>
</body>

</html>